summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/buftok.rb59
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/callback.rb58
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/channel.rb69
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/completion.rb304
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/connection.rb770
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable.rb210
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable/pool.rb2
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/file_watch.rb73
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/future.rb61
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/iterator.rb252
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/messages.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pool.rb151
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/process_watch.rb45
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/processes.rb123
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols.rb37
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb138
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb300
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb600
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb125
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb29
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb179
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb331
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb46
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb246
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb175
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb394
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb666
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb66
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb205
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pure_ruby.rb1284
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/queue.rb80
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb232
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/spawnable.rb84
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/streamer.rb118
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/threaded_resource.rb90
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/tick_loop.rb85
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/timers.rb61
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/version.rb3
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb1601
-rwxr-xr-xvendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/fastfilereaderext.bundlebin0 -> 57016 bytes
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/jeventmachine.rb316
-rwxr-xr-xvendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/rubyeventmachine.bundlebin0 -> 524152 bytes
43 files changed, 9788 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/buftok.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/buftok.rb
new file mode 100644
index 0000000..caf4f77
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/buftok.rb
@@ -0,0 +1,59 @@
+# BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
+# by default. It allows input to be spoon-fed from some outside source which
+# receives arbitrary length datagrams which may-or-may-not contain the token
+# by which entities are delimited. In this respect it's ideally paired with
+# something like EventMachine (http://rubyeventmachine.com/).
+class BufferedTokenizer
+ # New BufferedTokenizers will operate on lines delimited by a delimiter,
+ # which is by default the global input delimiter $/ ("\n").
+ #
+ # The input buffer is stored as an array. This is by far the most efficient
+ # approach given language constraints (in C a linked list would be a more
+ # appropriate data structure). Segments of input data are stored in a list
+ # which is only joined when a token is reached, substantially reducing the
+ # number of objects required for the operation.
+ def initialize(delimiter = $/)
+ @delimiter = delimiter
+ @input = []
+ @tail = ''
+ @trim = @delimiter.length - 1
+ end
+
+ # Extract takes an arbitrary string of input data and returns an array of
+ # tokenized entities, provided there were any available to extract. This
+ # makes for easy processing of datagrams using a pattern like:
+ #
+ # tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...
+ #
+ # Using -1 makes split to return "" if the token is at the end of
+ # the string, meaning the last element is the start of the next chunk.
+ def extract(data)
+ if @trim > 0
+ tail_end = @tail.slice!(-@trim, @trim) # returns nil if string is too short
+ data = tail_end + data if tail_end
+ end
+
+ @input << @tail
+ entities = data.split(@delimiter, -1)
+ @tail = entities.shift
+
+ unless entities.empty?
+ @input << @tail
+ entities.unshift @input.join
+ @input.clear
+ @tail = entities.pop
+ end
+
+ entities
+ end
+
+ # Flush the contents of the input buffer, i.e. return the input buffer even though
+ # a token has not yet been encountered
+ def flush
+ @input << @tail
+ buffer = @input.join
+ @input.clear
+ @tail = "" # @tail.clear is slightly faster, but not supported on 1.8.7
+ buffer
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/callback.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/callback.rb
new file mode 100644
index 0000000..4928feb
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/callback.rb
@@ -0,0 +1,58 @@
+module EventMachine
+ # Utility method for coercing arguments to an object that responds to :call.
+ # Accepts an object and a method name to send to, or a block, or an object
+ # that responds to :call.
+ #
+ # @example EventMachine.Callback used with a block. Returns that block.
+ #
+ # cb = EventMachine.Callback do |msg|
+ # puts(msg)
+ # end
+ # # returned object is a callable
+ # cb.call('hello world')
+ #
+ #
+ # @example EventMachine.Callback used with an object (to be more specific, class object) and a method name, returns an object that responds to #call
+ #
+ # cb = EventMachine.Callback(Object, :puts)
+ # # returned object is a callable that delegates to Kernel#puts (in this case Object.puts)
+ # cb.call('hello world')
+ #
+ #
+ # @example EventMachine.Callback used with an object that responds to #call. Returns the argument.
+ #
+ # cb = EventMachine.Callback(proc{ |msg| puts(msg) })
+ # # returned object is a callable
+ # cb.call('hello world')
+ #
+ #
+ # @overload Callback(object, method)
+ # Wraps `method` invocation on `object` into an object that responds to #call that proxies all the arguments to that method
+ # @param [Object] Object to invoke method on
+ # @param [Symbol] Method name
+ # @return [<#call>] An object that responds to #call that takes any number of arguments and invokes method on object with those arguments
+ #
+ # @overload Callback(object)
+ # Returns callable object as is, without any coercion
+ # @param [<#call>] An object that responds to #call
+ # @return [<#call>] Its argument
+ #
+ # @overload Callback(&block)
+ # Returns block passed to it without any coercion
+ # @return [<#call>] Block passed to this method
+ #
+ # @raise [ArgumentError] When argument doesn't respond to #call, method name is missing or when invoked without arguments and block isn't given
+ #
+ # @return [<#call>]
+ def self.Callback(object = nil, method = nil, &blk)
+ if object && method
+ lambda { |*args| object.__send__ method, *args }
+ else
+ if object.respond_to? :call
+ object
+ else
+ blk || raise(ArgumentError)
+ end # if
+ end # if
+ end # self.Callback
+end # EventMachine
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/channel.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/channel.rb
new file mode 100644
index 0000000..a919adf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/channel.rb
@@ -0,0 +1,69 @@
+module EventMachine
+ # Provides a simple thread-safe way to transfer data between (typically) long running
+ # tasks in {EventMachine.defer} and event loop thread.
+ #
+ # @example
+ #
+ # channel = EventMachine::Channel.new
+ # sid = channel.subscribe { |msg| p [:got, msg] }
+ #
+ # channel.push('hello world')
+ # channel.unsubscribe(sid)
+ #
+ #
+ class Channel
+ def initialize
+ @subs = {}
+ @uid = 0
+ end
+
+ # Return the number of current subscribers.
+ def num_subscribers
+ return @subs.size
+ end
+
+ # Takes any arguments suitable for EM::Callback() and returns a subscriber
+ # id for use when unsubscribing.
+ #
+ # @return [Integer] Subscribe identifier
+ # @see #unsubscribe
+ def subscribe(*a, &b)
+ name = gen_id
+ EM.schedule { @subs[name] = EM::Callback(*a, &b) }
+
+ name
+ end
+
+ # Removes subscriber from the list.
+ #
+ # @param [Integer] Subscriber identifier
+ # @see #subscribe
+ def unsubscribe(name)
+ EM.schedule { @subs.delete name }
+ end
+
+ # Add items to the channel, which are pushed out to all subscribers.
+ def push(*items)
+ items = items.dup
+ EM.schedule { items.each { |i| @subs.values.each { |s| s.call i } } }
+ end
+ alias << push
+
+ # Fetches one message from the channel.
+ def pop(*a, &b)
+ EM.schedule {
+ name = subscribe do |*args|
+ unsubscribe(name)
+ EM::Callback(*a, &b).call(*args)
+ end
+ }
+ end
+
+ private
+
+ # @private
+ def gen_id
+ @uid += 1
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/completion.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/completion.rb
new file mode 100644
index 0000000..926535f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/completion.rb
@@ -0,0 +1,304 @@
+# = EM::Completion
+#
+# A completion is a callback container for various states of completion. In
+# its most basic form it has a start state and a finish state.
+#
+# This implementation includes some hold-back from the EM::Deferrable
+# interface in order to be compatible - but it has a much cleaner
+# implementation.
+#
+# In general it is preferred that this implementation be used as a state
+# callback container than EM::DefaultDeferrable or other classes including
+# EM::Deferrable. This is because it is generally more sane to keep this level
+# of state in a dedicated state-back container. This generally leads to more
+# malleable interfaces and software designs, as well as eradicating nasty bugs
+# that result from abstraction leakage.
+#
+# == Basic Usage
+#
+# As already mentioned, the basic usage of a Completion is simply for its two
+# final states, :succeeded and :failed.
+#
+# An asynchronous operation will complete at some future point in time, and
+# users often want to react to this event. API authors will want to expose
+# some common interface to react to these events.
+#
+# In the following example, the user wants to know when a short lived
+# connection has completed its exchange with the remote server. The simple
+# protocol just waits for an ack to its message.
+#
+# class Protocol < EM::Connection
+# include EM::P::LineText2
+#
+# def initialize(message, completion)
+# @message, @completion = message, completion
+# @completion.completion { close_connection }
+# @completion.timeout(1, :timeout)
+# end
+#
+# def post_init
+# send_data(@message)
+# end
+#
+# def receive_line(line)
+# case line
+# when /ACK/i
+# @completion.succeed line
+# when /ERR/i
+# @completion.fail :error, line
+# else
+# @completion.fail :unknown, line
+# end
+# end
+#
+# def unbind
+# @completion.fail :disconnected unless @completion.completed?
+# end
+# end
+#
+# class API
+# attr_reader :host, :port
+#
+# def initialize(host = 'example.org', port = 8000)
+# @host, @port = host, port
+# end
+#
+# def request(message)
+# completion = EM::Deferrable::Completion.new
+# EM.connect(host, port, Protocol, message, completion)
+# completion
+# end
+# end
+#
+# api = API.new
+# completion = api.request('stuff')
+# completion.callback do |line|
+# puts "API responded with: #{line}"
+# end
+# completion.errback do |type, line|
+# case type
+# when :error
+# puts "API error: #{line}"
+# when :unknown
+# puts "API returned unknown response: #{line}"
+# when :disconnected
+# puts "API server disconnected prematurely"
+# when :timeout
+# puts "API server did not respond in a timely fashion"
+# end
+# end
+#
+# == Advanced Usage
+#
+# This completion implementation also supports more state callbacks and
+# arbitrary states (unlike the original Deferrable API). This allows for basic
+# stateful process encapsulation. One might use this to setup state callbacks
+# for various states in an exchange like in the basic usage example, except
+# where the applicaiton could be made to react to "connected" and
+# "disconnected" states additionally.
+#
+# class Protocol < EM::Connection
+# def initialize(completion)
+# @response = []
+# @completion = completion
+# @completion.stateback(:disconnected) do
+# @completion.succeed @response.join
+# end
+# end
+#
+# def connection_completed
+# @host, @port = Socket.unpack_sockaddr_in get_peername
+# @completion.change_state(:connected, @host, @port)
+# send_data("GET http://example.org/ HTTP/1.0\r\n\r\n")
+# end
+#
+# def receive_data(data)
+# @response << data
+# end
+#
+# def unbind
+# @completion.change_state(:disconnected, @host, @port)
+# end
+# end
+#
+# completion = EM::Deferrable::Completion.new
+# completion.stateback(:connected) do |host, port|
+# puts "Connected to #{host}:#{port}"
+# end
+# completion.stateback(:disconnected) do |host, port|
+# puts "Disconnected from #{host}:#{port}"
+# end
+# completion.callback do |response|
+# puts response
+# end
+#
+# EM.connect('example.org', 80, Protocol, completion)
+#
+# == Timeout
+#
+# The Completion also has a timeout. The timeout is global and is not aware of
+# states apart from completion states. The timeout is only engaged if #timeout
+# is called, and it will call fail if it is reached.
+#
+# == Completion states
+#
+# By default there are two completion states, :succeeded and :failed. These
+# states can be modified by subclassing and overrding the #completion_states
+# method. Completion states are special, in that callbacks for all completion
+# states are explcitly cleared when a completion state is entered. This
+# prevents errors that could arise from accidental unterminated timeouts, and
+# other such user errors.
+#
+# == Other notes
+#
+# Several APIs have been carried over from EM::Deferrable for compatibility
+# reasons during a transitionary period. Specifically cancel_errback and
+# cancel_callback are implemented, but their usage is to be strongly
+# discouraged. Due to the already complex nature of reaction systems, dynamic
+# callback deletion only makes the problem much worse. It is always better to
+# add correct conditionals to the callback code, or use more states, than to
+# address such implementaiton issues with conditional callbacks.
+
+module EventMachine
+
+ class Completion
+ # This is totally not used (re-implemented), it's here in case people check
+ # for kind_of?
+ include EventMachine::Deferrable
+
+ attr_reader :state, :value
+
+ def initialize
+ @state = :unknown
+ @callbacks = Hash.new { |h,k| h[k] = [] }
+ @value = []
+ @timeout_timer = nil
+ end
+
+ # Enter the :succeeded state, setting the result value if given.
+ def succeed(*args)
+ change_state(:succeeded, *args)
+ end
+ # The old EM method:
+ alias set_deferred_success succeed
+
+ # Enter the :failed state, setting the result value if given.
+ def fail(*args)
+ change_state(:failed, *args)
+ end
+ # The old EM method:
+ alias set_deferred_failure fail
+
+ # Statebacks are called when you enter (or are in) the named state.
+ def stateback(state, *a, &b)
+ # The following is quite unfortunate special casing for :completed
+ # statebacks, but it's a necessary evil for latent completion
+ # definitions.
+
+ if :completed == state || !completed? || @state == state
+ @callbacks[state] << EM::Callback(*a, &b)
+ end
+ execute_callbacks
+ self
+ end
+
+ # Callbacks are called when you enter (or are in) a :succeeded state.
+ def callback(*a, &b)
+ stateback(:succeeded, *a, &b)
+ end
+
+ # Errbacks are called when you enter (or are in) a :failed state.
+ def errback(*a, &b)
+ stateback(:failed, *a, &b)
+ end
+
+ # Completions are called when you enter (or are in) either a :failed or a
+ # :succeeded state. They are stored as a special (reserved) state called
+ # :completed.
+ def completion(*a, &b)
+ stateback(:completed, *a, &b)
+ end
+
+ # Enter a new state, setting the result value if given. If the state is one
+ # of :succeeded or :failed, then :completed callbacks will also be called.
+ def change_state(state, *args)
+ @value = args
+ @state = state
+
+ EM.schedule { execute_callbacks }
+ end
+
+ # The old EM method:
+ alias set_deferred_status change_state
+
+ # Indicates that we've reached some kind of completion state, by default
+ # this is :succeeded or :failed. Due to these semantics, the :completed
+ # state is reserved for internal use.
+ def completed?
+ completion_states.any? { |s| state == s }
+ end
+
+ # Completion states simply returns a list of completion states, by default
+ # this is :succeeded and :failed.
+ def completion_states
+ [:succeeded, :failed]
+ end
+
+ # Schedule a time which if passes before we enter a completion state, this
+ # deferrable will be failed with the given arguments.
+ def timeout(time, *args)
+ cancel_timeout
+ @timeout_timer = EM::Timer.new(time) do
+ fail(*args) unless completed?
+ end
+ end
+
+ # Disable the timeout
+ def cancel_timeout
+ if @timeout_timer
+ @timeout_timer.cancel
+ @timeout_timer = nil
+ end
+ end
+
+ # Remove an errback. N.B. Some errbacks cannot be deleted. Usage is NOT
+ # recommended, this is an anti-pattern.
+ def cancel_errback(*a, &b)
+ @callbacks[:failed].delete(EM::Callback(*a, &b))
+ end
+
+ # Remove a callback. N.B. Some callbacks cannot be deleted. Usage is NOT
+ # recommended, this is an anti-pattern.
+ def cancel_callback(*a, &b)
+ @callbacks[:succeeded].delete(EM::Callback(*a, &b))
+ end
+
+ private
+ # Execute all callbacks for the current state. If in a completed state, then
+ # call any statebacks associated with the completed state.
+ def execute_callbacks
+ execute_state_callbacks(state)
+ if completed?
+ execute_state_callbacks(:completed)
+ clear_dead_callbacks
+ cancel_timeout
+ end
+ end
+
+ # Iterate all callbacks for a given state, and remove then call them.
+ def execute_state_callbacks(state)
+ while callback = @callbacks[state].shift
+ callback.call(*value)
+ end
+ end
+
+ # If we enter a completion state, clear other completion states after all
+ # callback chains are completed. This means that operation specific
+ # callbacks can't be dual-called, which is most common user error.
+ def clear_dead_callbacks
+ completion_states.each do |state|
+ @callbacks[state].clear
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/connection.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/connection.rb
new file mode 100644
index 0000000..267aec2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/connection.rb
@@ -0,0 +1,770 @@
+module EventMachine
+ class FileNotFoundException < Exception
+ end
+
+ # EventMachine::Connection is a class that is instantiated
+ # by EventMachine's processing loop whenever a new connection
+ # is created. (New connections can be either initiated locally
+ # to a remote server or accepted locally from a remote client.)
+ # When a Connection object is instantiated, it <i>mixes in</i>
+ # the functionality contained in the user-defined module
+ # specified in calls to {EventMachine.connect} or {EventMachine.start_server}.
+ # User-defined handler modules may redefine any or all of the standard
+ # methods defined here, as well as add arbitrary additional code
+ # that will also be mixed in.
+ #
+ # EventMachine manages one object inherited from EventMachine::Connection
+ # (and containing the mixed-in user code) for every network connection
+ # that is active at any given time.
+ # The event loop will automatically call methods on EventMachine::Connection
+ # objects whenever specific events occur on the corresponding connections,
+ # as described below.
+ #
+ # This class is never instantiated by user code, and does not publish an
+ # initialize method. The instance methods of EventMachine::Connection
+ # which may be called by the event loop are:
+ #
+ # * {#post_init}
+ # * {#connection_completed}
+ # * {#receive_data}
+ # * {#unbind}
+ # * {#ssl_verify_peer} (if TLS is used)
+ # * {#ssl_handshake_completed}
+ #
+ # All of the other instance methods defined here are called only by user code.
+ #
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ class Connection
+ # @private
+ attr_accessor :signature
+
+ # @private
+ alias original_method method
+
+ # Override .new so subclasses don't have to call super and can ignore
+ # connection-specific arguments
+ #
+ # @private
+ def self.new(sig, *args)
+ allocate.instance_eval do
+ # Store signature
+ @signature = sig
+ # associate_callback_target sig
+
+ # Call a superclass's #initialize if it has one
+ initialize(*args)
+
+ # post initialize callback
+ post_init
+
+ self
+ end
+ end
+
+ # Stubbed initialize so legacy superclasses can safely call super
+ #
+ # @private
+ def initialize(*args)
+ end
+
+ # Called by the event loop immediately after the network connection has been established,
+ # and before resumption of the network loop.
+ # This method is generally not called by user code, but is called automatically
+ # by the event loop. The base-class implementation is a no-op.
+ # This is a very good place to initialize instance variables that will
+ # be used throughout the lifetime of the network connection.
+ #
+ # @see #connection_completed
+ # @see #unbind
+ # @see #send_data
+ # @see #receive_data
+ def post_init
+ end
+
+ # Called by the event loop whenever data has been received by the network connection.
+ # It is never called by user code. {#receive_data} is called with a single parameter, a String containing
+ # the network protocol data, which may of course be binary. You will
+ # generally redefine this method to perform your own processing of the incoming data.
+ #
+ # Here's a key point which is essential to understanding the event-driven
+ # programming model: <i>EventMachine knows absolutely nothing about the protocol
+ # which your code implements.</i> You must not make any assumptions about
+ # the size of the incoming data packets, or about their alignment on any
+ # particular intra-message or PDU boundaries (such as line breaks).
+ # receive_data can and will send you arbitrary chunks of data, with the
+ # only guarantee being that the data is presented to your code in the order
+ # it was collected from the network. Don't even assume that the chunks of
+ # data will correspond to network packets, as EventMachine can and will coalesce
+ # several incoming packets into one, to improve performance. The implication for your
+ # code is that you generally will need to implement some kind of a state machine
+ # in your redefined implementation of receive_data. For a better understanding
+ # of this, read through the examples of specific protocol handlers in EventMachine::Protocols
+ #
+ # The base-class implementation (which will be invoked only if you didn't override it in your protocol handler)
+ # simply prints incoming data packet size to stdout.
+ #
+ # @param [String] data Opaque incoming data.
+ # @note Depending on the protocol, buffer sizes and OS networking stack configuration, incoming data may or may not be "a complete message".
+ # It is up to this handler to detect content boundaries to determine whether all the content (for example, full HTTP request)
+ # has been received and can be processed.
+ #
+ # @see #post_init
+ # @see #connection_completed
+ # @see #unbind
+ # @see #send_data
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ def receive_data data
+ puts "............>>>#{data.length}"
+ end
+
+ # Called by EventMachine when the SSL/TLS handshake has
+ # been completed, as a result of calling #start_tls to initiate SSL/TLS on the connection.
+ #
+ # This callback exists because {#post_init} and {#connection_completed} are **not** reliable
+ # for indicating when an SSL/TLS connection is ready to have its certificate queried for.
+ #
+ # @see #get_peer_cert
+ def ssl_handshake_completed
+ end
+
+ # Called by EventMachine when :verify_peer => true has been passed to {#start_tls}.
+ # It will be called with each certificate in the certificate chain provided by the remote peer.
+ #
+ # The cert will be passed as a String in PEM format, the same as in {#get_peer_cert}. It is up to user defined
+ # code to perform a check on the certificates. The return value from this callback is used to accept or deny the peer.
+ # A return value that is not nil or false triggers acceptance. If the peer is not accepted, the connection
+ # will be subsequently closed.
+ #
+ # @example This server always accepts all peers
+ #
+ # module AcceptServer
+ # def post_init
+ # start_tls(:verify_peer => true)
+ # end
+ #
+ # def ssl_verify_peer(cert)
+ # true
+ # end
+ #
+ # def ssl_handshake_completed
+ # $server_handshake_completed = true
+ # end
+ # end
+ #
+ #
+ # @example This server never accepts any peers
+ #
+ # module DenyServer
+ # def post_init
+ # start_tls(:verify_peer => true)
+ # end
+ #
+ # def ssl_verify_peer(cert)
+ # # Do not accept the peer. This should now cause the connection to shut down
+ # # without the SSL handshake being completed.
+ # false
+ # end
+ #
+ # def ssl_handshake_completed
+ # $server_handshake_completed = true
+ # end
+ # end
+ #
+ # @see #start_tls
+ def ssl_verify_peer(cert)
+ end
+
+ # called by the framework whenever a connection (either a server or client connection) is closed.
+ # The close can occur because your code intentionally closes it (using {#close_connection} and {#close_connection_after_writing}),
+ # because the remote peer closed the connection, or because of a network error.
+ # You may not assume that the network connection is still open and able to send or
+ # receive data when the callback to unbind is made. This is intended only to give
+ # you a chance to clean up associations your code may have made to the connection
+ # object while it was open.
+ #
+ # If you want to detect which peer has closed the connection, you can override {#close_connection} in your protocol handler
+ # and set an @ivar.
+ #
+ # @example Overriding Connection#close_connection to distinguish connections closed on our side
+ #
+ # class MyProtocolHandler < EventMachine::Connection
+ #
+ # # ...
+ #
+ # def close_connection(*args)
+ # @intentionally_closed_connection = true
+ # super(*args)
+ # end
+ #
+ # def unbind
+ # if @intentionally_closed_connection
+ # # ...
+ # end
+ # end
+ #
+ # # ...
+ #
+ # end
+ #
+ # @see #post_init
+ # @see #connection_completed
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ def unbind
+ end
+
+ # Called by the reactor after attempting to relay incoming data to a descriptor (set as a proxy target descriptor with
+ # {EventMachine.enable_proxy}) that has already been closed.
+ #
+ # @see EventMachine.enable_proxy
+ def proxy_target_unbound
+ end
+
+ # called when the reactor finished proxying all
+ # of the requested bytes.
+ def proxy_completed
+ end
+
+ # EventMachine::Connection#proxy_incoming_to is called only by user code. It sets up
+ # a low-level proxy relay for all data inbound for this connection, to the connection given
+ # as the argument. This is essentially just a helper method for enable_proxy.
+ #
+ # @see EventMachine.enable_proxy
+ def proxy_incoming_to(conn,bufsize=0)
+ EventMachine::enable_proxy(self, conn, bufsize)
+ end
+
+ # A helper method for {EventMachine.disable_proxy}
+ def stop_proxying
+ EventMachine::disable_proxy(self)
+ end
+
+ # The number of bytes proxied to another connection. Reset to zero when
+ # EventMachine::Connection#proxy_incoming_to is called, and incremented whenever data is proxied.
+ def get_proxied_bytes
+ EventMachine::get_proxied_bytes(@signature)
+ end
+
+ # EventMachine::Connection#close_connection is called only by user code, and never
+ # by the event loop. You may call this method against a connection object in any
+ # callback handler, whether or not the callback was made against the connection
+ # you want to close. close_connection <i>schedules</i> the connection to be closed
+ # at the next available opportunity within the event loop. You may not assume that
+ # the connection is closed when close_connection returns. In particular, the framework
+ # will callback the unbind method for the particular connection at a point shortly
+ # after you call close_connection. You may assume that the unbind callback will
+ # take place sometime after your call to close_connection completes. In other words,
+ # the unbind callback will not re-enter your code "inside" of your call to close_connection.
+ # However, it's not guaranteed that a future version of EventMachine will not change
+ # this behavior.
+ #
+ # {#close_connection} will *silently discard* any outbound data which you have
+ # sent to the connection using {EventMachine::Connection#send_data} but which has not
+ # yet been sent across the network. If you want to avoid this behavior, use
+ # {EventMachine::Connection#close_connection_after_writing}.
+ #
+ def close_connection after_writing = false
+ EventMachine::close_connection @signature, after_writing
+ end
+
+ # Removes given connection from the event loop.
+ # The connection's socket remains open and its file descriptor number is returned.
+ def detach
+ EventMachine::detach_fd @signature
+ end
+
+ def get_sock_opt level, option
+ EventMachine::get_sock_opt @signature, level, option
+ end
+
+ def set_sock_opt level, optname, optval
+ EventMachine::set_sock_opt @signature, level, optname, optval
+ end
+
+ # A variant of {#close_connection}.
+ # All of the descriptive comments given for close_connection also apply to
+ # close_connection_after_writing, *with one exception*: if the connection has
+ # outbound data sent using send_dat but which has not yet been sent across the network,
+ # close_connection_after_writing will schedule the connection to be closed *after*
+ # all of the outbound data has been safely written to the remote peer.
+ #
+ # Depending on the amount of outgoing data and the speed of the network,
+ # considerable time may elapse between your call to close_connection_after_writing
+ # and the actual closing of the socket (at which time the unbind callback will be called
+ # by the event loop). During this time, you *may not* call send_data to transmit
+ # additional data (that is, the connection is closed for further writes). In very
+ # rare cases, you may experience a receive_data callback after your call to {#close_connection_after_writing},
+ # depending on whether incoming data was in the process of being received on the connection
+ # at the moment when you called {#close_connection_after_writing}. Your protocol handler must
+ # be prepared to properly deal with such data (probably by ignoring it).
+ #
+ # @see #close_connection
+ # @see #send_data
+ def close_connection_after_writing
+ close_connection true
+ end
+
+ # Call this method to send data to the remote end of the network connection. It takes a single String argument,
+ # which may contain binary data. Data is buffered to be sent at the end of this event loop tick (cycle).
+ #
+ # When used in a method that is event handler (for example, {#post_init} or {#connection_completed}, it will send
+ # data to the other end of the connection that generated the event.
+ # You can also call {#send_data} to write to other connections. For more information see The Chat Server Example in the
+ # {file:docs/GettingStarted.md EventMachine tutorial}.
+ #
+ # If you want to send some data and then immediately close the connection, make sure to use {#close_connection_after_writing}
+ # instead of {#close_connection}.
+ #
+ #
+ # @param [String] data Data to send asynchronously
+ #
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ # @see Connection#receive_data
+ # @see Connection#post_init
+ # @see Connection#unbind
+ def send_data data
+ data = data.to_s
+ size = data.bytesize if data.respond_to?(:bytesize)
+ size ||= data.size
+ EventMachine::send_data @signature, data, size
+ end
+
+ # Returns true if the connection is in an error state, false otherwise.
+ #
+ # In general, you can detect the occurrence of communication errors or unexpected
+ # disconnection by the remote peer by handing the {#unbind} method. In some cases, however,
+ # it's useful to check the status of the connection using {#error?} before attempting to send data.
+ # This function is synchronous but it will return immediately without blocking.
+ #
+ # @return [Boolean] true if the connection is in an error state, false otherwise
+ def error?
+ errno = EventMachine::report_connection_error_status(@signature)
+ case errno
+ when 0
+ false
+ when -1
+ true
+ else
+ EventMachine::ERRNOS[errno]
+ end
+ end
+
+ # Called by the event loop when a remote TCP connection attempt completes successfully.
+ # You can expect to get this notification after calls to {EventMachine.connect}. Remember that EventMachine makes remote connections
+ # asynchronously, just as with any other kind of network event. This method
+ # is intended primarily to assist with network diagnostics. For normal protocol
+ # handling, use #post_init to perform initial work on a new connection (such as sending initial set of data).
+ # {Connection#post_init} will always be called. This method will only be called in case of a successful completion.
+ # A connection attempt which fails will result a call to {Connection#unbind} after the failure.
+ #
+ # @see Connection#post_init
+ # @see Connection#unbind
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ def connection_completed
+ end
+
+ # Call {#start_tls} at any point to initiate TLS encryption on connected streams.
+ # The method is smart enough to know whether it should perform a server-side
+ # or a client-side handshake. An appropriate place to call {#start_tls} is in
+ # your redefined {#post_init} method, or in the {#connection_completed} handler for
+ # an outbound connection.
+ #
+ #
+ # @option args [String] :cert_chain_file (nil) local path of a readable file that contants a chain of X509 certificates in
+ # the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail),
+ # with the most-resolved certificate at the top of the file, successive intermediate
+ # certs in the middle, and the root (or CA) cert at the bottom.
+ #
+ # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail).
+ #
+ # @option args [Boolean] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code.
+ # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate
+ # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this.
+ #
+ # @option args [Boolean] :fail_if_no_peer_cert (false) Used in conjunction with verify_peer. If set the SSL handshake will be terminated if the peer does not provide a certificate.
+ #
+ #
+ # @option args [String] :cipher_list ("ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH") indicates the available SSL cipher values. Default value is "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH". Check the format of the OpenSSL cipher string at http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT.
+ #
+ # @option args [String] :ecdh_curve (nil) The curve for ECDHE ciphers. See available ciphers with 'openssl ecparam -list_curves'
+ #
+ # @option args [String] :dhparam (nil) The local path of a file containing DH parameters for EDH ciphers in [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) See: 'openssl dhparam'
+ #
+ # @option args [Array] :ssl_version (TLSv1 TLSv1_1 TLSv1_2) indicates the allowed SSL/TLS versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1_1}, {TLSv1_2}.
+ #
+ # @example Using TLS with EventMachine
+ #
+ # require 'rubygems'
+ # require 'eventmachine'
+ #
+ # module Handler
+ # def post_init
+ # start_tls(:private_key_file => '/tmp/server.key', :cert_chain_file => '/tmp/server.crt', :verify_peer => false)
+ # end
+ # end
+ #
+ # EventMachine.run do
+ # EventMachine.start_server("127.0.0.1", 9999, Handler)
+ # end
+ #
+ # @param [Hash] args
+ #
+ # @todo support passing an encryption parameter, which can be string or Proc, to get a passphrase
+ # for encrypted private keys.
+ # @todo support passing key material via raw strings or Procs that return strings instead of
+ # just filenames.
+ #
+ # @see #ssl_verify_peer
+ def start_tls args={}
+ priv_key = args[:private_key_file]
+ cert_chain = args[:cert_chain_file]
+ verify_peer = args[:verify_peer]
+ sni_hostname = args[:sni_hostname]
+ cipher_list = args[:cipher_list]
+ ssl_version = args[:ssl_version]
+ ecdh_curve = args[:ecdh_curve]
+ dhparam = args[:dhparam]
+ fail_if_no_peer_cert = args[:fail_if_no_peer_cert]
+
+ [priv_key, cert_chain].each do |file|
+ next if file.nil? or file.empty?
+ raise FileNotFoundException,
+ "Could not find #{file} for start_tls" unless File.exist? file
+ end
+
+ protocols_bitmask = 0
+ if ssl_version.nil?
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
+ else
+ [ssl_version].flatten.each do |p|
+ case p.to_s.downcase
+ when 'sslv2'
+ protocols_bitmask |= EventMachine::EM_PROTO_SSLv2
+ when 'sslv3'
+ protocols_bitmask |= EventMachine::EM_PROTO_SSLv3
+ when 'tlsv1'
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
+ when 'tlsv1_1'
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
+ when 'tlsv1_2'
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
+ else
+ raise("Unrecognized SSL/TLS Protocol: #{p}")
+ end
+ end
+ end
+
+ EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask)
+ EventMachine::start_tls @signature
+ end
+
+ # If [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security) is active on the connection, returns the remote [X509 certificate](http://en.wikipedia.org/wiki/X.509)
+ # as a string, in the popular [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail). This can then be used for arbitrary validation
+ # of a peer's certificate in your code.
+ #
+ # This should be called in/after the {#ssl_handshake_completed} callback, which indicates
+ # that SSL/TLS is active. Using this callback is important, because the certificate may not
+ # be available until the time it is executed. Using #post_init or #connection_completed is
+ # not adequate, because the SSL handshake may still be taking place.
+ #
+ # This method will return `nil` if:
+ #
+ # * EventMachine is not built with [OpenSSL](http://www.openssl.org) support
+ # * [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security) is not active on the connection
+ # * TLS handshake is not yet complete
+ # * Remote peer for any other reason has not presented a certificate
+ #
+ #
+ # @example Getting peer TLS certificate information in EventMachine
+ #
+ # module Handler
+ # def post_init
+ # puts "Starting TLS"
+ # start_tls
+ # end
+ #
+ # def ssl_handshake_completed
+ # puts get_peer_cert
+ # close_connection
+ # end
+ #
+ # def unbind
+ # EventMachine::stop_event_loop
+ # end
+ # end
+ #
+ # EventMachine.run do
+ # EventMachine.connect "mail.google.com", 443, Handler
+ # end
+ #
+ # # Will output:
+ # # -----BEGIN CERTIFICATE-----
+ # # MIIDIjCCAougAwIBAgIQbldpChBPqv+BdPg4iwgN8TANBgkqhkiG9w0BAQUFADBM
+ # # MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
+ # # THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wODA1MDIxNjMyNTRaFw0w
+ # # OTA1MDIxNjMyNTRaMGkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
+ # # MRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRgw
+ # # FgYDVQQDEw9tYWlsLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+ # # AoGBALlkxdh2QXegdElukCSOV2+8PKiONIS+8Tu9K7MQsYpqtLNC860zwOPQ2NLI
+ # # 3Zp4jwuXVTrtzGuiqf5Jioh35Ig3CqDXtLyZoypjZUQcq4mlLzHlhIQ4EhSjDmA7
+ # # Ffw9y3ckSOQgdBQWNLbquHh9AbEUjmhkrYxIqKXeCnRKhv6nAgMBAAGjgecwgeQw
+ # # KAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwNgYDVR0f
+ # # BC8wLTAroCmgJ4YlaHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVNHQ0NBLmNy
+ # # bDByBggrBgEFBQcBAQRmMGQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0
+ # # ZS5jb20wPgYIKwYBBQUHMAKGMmh0dHA6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0
+ # # b3J5L1RoYXd0ZV9TR0NfQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEF
+ # # BQADgYEAsRwpLg1dgCR1gYDK185MFGukXMeQFUvhGqF8eT/CjpdvezyKVuz84gSu
+ # # 6ccMXgcPQZGQN/F4Xug+Q01eccJjRSVfdvR5qwpqCj+6BFl5oiKDBsveSkrmL5dz
+ # # s2bn7TdTSYKcLeBkjXxDLHGBqLJ6TNCJ3c4/cbbG5JhGvoema94=
+ # # -----END CERTIFICATE-----
+ #
+ # You can do whatever you want with the certificate String, such as load it
+ # as a certificate object using the OpenSSL library, and check its fields.
+ #
+ # @return [String] the remote [X509 certificate](http://en.wikipedia.org/wiki/X.509), in the popular [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail),
+ # if TLS is active on the connection
+ #
+ # @see Connection#start_tls
+ # @see Connection#ssl_handshake_completed
+ def get_peer_cert
+ EventMachine::get_peer_cert @signature
+ end
+
+ def get_cipher_bits
+ EventMachine::get_cipher_bits @signature
+ end
+
+ def get_cipher_name
+ EventMachine::get_cipher_name @signature
+ end
+
+ def get_cipher_protocol
+ EventMachine::get_cipher_protocol @signature
+ end
+
+ def get_sni_hostname
+ EventMachine::get_sni_hostname @signature
+ end
+
+ # Sends UDP messages.
+ #
+ # This method may be called from any Connection object that refers
+ # to an open datagram socket (see EventMachine#open_datagram_socket).
+ # The method sends a UDP (datagram) packet containing the data you specify,
+ # to a remote peer specified by the IP address and port that you give
+ # as parameters to the method.
+ # Observe that you may send a zero-length packet (empty string).
+ # However, you may not send an arbitrarily-large data packet because
+ # your operating system will enforce a platform-specific limit on
+ # the size of the outbound packet. (Your kernel
+ # will respond in a platform-specific way if you send an overlarge
+ # packet: some will send a truncated packet, some will complain, and
+ # some will silently drop your request).
+ # On LANs, it's usually OK to send datagrams up to about 4000 bytes in length,
+ # but to be really safe, send messages smaller than the Ethernet-packet
+ # size (typically about 1400 bytes). Some very restrictive WANs
+ # will either drop or truncate packets larger than about 500 bytes.
+ #
+ # @param [String] data Data to send asynchronously
+ # @param [String] recipient_address IP address of the recipient
+ # @param [String] recipient_port Port of the recipient
+ def send_datagram data, recipient_address, recipient_port
+ data = data.to_s
+ size = data.bytesize if data.respond_to?(:bytesize)
+ size ||= data.size
+ EventMachine::send_datagram @signature, data, size, recipient_address, Integer(recipient_port)
+ end
+
+
+ # This method is used with stream-connections to obtain the identity
+ # of the remotely-connected peer. If a peername is available, this method
+ # returns a sockaddr structure. The method returns nil if no peername is available.
+ # You can use Socket.unpack_sockaddr_in and its variants to obtain the
+ # values contained in the peername structure returned from #get_peername.
+ #
+ # @example How to get peer IP address and port with EventMachine
+ #
+ # require 'socket'
+ #
+ # module Handler
+ # def receive_data data
+ # port, ip = Socket.unpack_sockaddr_in(get_peername)
+ # puts "got #{data.inspect} from #{ip}:#{port}"
+ # end
+ # end
+ def get_peername
+ EventMachine::get_peername @signature
+ end
+
+ # Used with stream-connections to obtain the identity
+ # of the local side of the connection. If a local name is available, this method
+ # returns a sockaddr structure. The method returns nil if no local name is available.
+ # You can use {Socket.unpack_sockaddr_in} and its variants to obtain the
+ # values contained in the local-name structure returned from this method.
+ #
+ # @example
+ #
+ # require 'socket'
+ #
+ # module Handler
+ # def receive_data data
+ # port, ip = Socket.unpack_sockaddr_in(get_sockname)
+ # puts "got #{data.inspect}"
+ # end
+ # end
+ def get_sockname
+ EventMachine::get_sockname @signature
+ end
+
+ # Returns the PID (kernel process identifier) of a subprocess
+ # associated with this Connection object. For use with {EventMachine.popen}
+ # and similar methods. Returns nil when there is no meaningful subprocess.
+ #
+ # @return [Integer]
+ def get_pid
+ EventMachine::get_subprocess_pid @signature
+ end
+
+ # Returns a subprocess exit status. Only useful for {EventMachine.popen}. Call it in your
+ # {#unbind} handler.
+ #
+ # @return [Integer]
+ def get_status
+ EventMachine::get_subprocess_status @signature
+ end
+
+ # The number of seconds since the last send/receive activity on this connection.
+ def get_idle_time
+ EventMachine::get_idle_time @signature
+ end
+
+ # comm_inactivity_timeout returns the current value (float in seconds) of the inactivity-timeout
+ # property of network-connection and datagram-socket objects. A nonzero value
+ # indicates that the connection or socket will automatically be closed if no read or write
+ # activity takes place for at least that number of seconds.
+ # A zero value (the default) specifies that no automatic timeout will take place.
+ def comm_inactivity_timeout
+ EventMachine::get_comm_inactivity_timeout @signature
+ end
+
+ # Allows you to set the inactivity-timeout property for
+ # a network connection or datagram socket. Specify a non-negative float value in seconds.
+ # If the value is greater than zero, the connection or socket will automatically be closed
+ # if no read or write activity takes place for at least that number of seconds.
+ # Specify a value of zero to indicate that no automatic timeout should take place.
+ # Zero is the default value.
+ def comm_inactivity_timeout= value
+ EventMachine::set_comm_inactivity_timeout @signature, value.to_f
+ end
+ alias set_comm_inactivity_timeout comm_inactivity_timeout=
+
+ # The duration after which a TCP connection in the connecting state will fail.
+ # It is important to distinguish this value from {EventMachine::Connection#comm_inactivity_timeout},
+ # which looks at how long since data was passed on an already established connection.
+ # The value is a float in seconds.
+ #
+ # @return [Float] The duration after which a TCP connection in the connecting state will fail, in seconds.
+ def pending_connect_timeout
+ EventMachine::get_pending_connect_timeout @signature
+ end
+
+ # Sets the duration after which a TCP connection in a
+ # connecting state will fail.
+ #
+ # @param [Float, #to_f] value Connection timeout in seconds
+ def pending_connect_timeout= value
+ EventMachine::set_pending_connect_timeout @signature, value.to_f
+ end
+ alias set_pending_connect_timeout pending_connect_timeout=
+
+ # Reconnect to a given host/port with the current instance
+ #
+ # @param [String] server Hostname or IP address
+ # @param [Integer] port Port to reconnect to
+ def reconnect server, port
+ EventMachine::reconnect server, port, self
+ end
+
+
+ # Like {EventMachine::Connection#send_data}, this sends data to the remote end of
+ # the network connection. {EventMachine::Connection#send_file_data} takes a
+ # filename as an argument, though, and sends the contents of the file, in one
+ # chunk.
+ #
+ # @param [String] filename Local path of the file to send
+ #
+ # @see #send_data
+ # @author Kirk Haines
+ def send_file_data filename
+ EventMachine::send_file_data @signature, filename
+ end
+
+ # Open a file on the filesystem and send it to the remote peer. This returns an
+ # object of type {EventMachine::Deferrable}. The object's callbacks will be executed
+ # on the reactor main thread when the file has been completely scheduled for
+ # transmission to the remote peer. Its errbacks will be called in case of an error (such as file-not-found).
+ # This method employs various strategies to achieve the fastest possible performance,
+ # balanced against minimum consumption of memory.
+ #
+ # Warning: this feature has an implicit dependency on an outboard extension,
+ # evma_fastfilereader. You must install this extension in order to use {#stream_file_data}
+ # with files larger than a certain size (currently 8192 bytes).
+ #
+ # @option args [Boolean] :http_chunks (false) If true, this method will stream the file data in a format
+ # compatible with the HTTP chunked-transfer encoding
+ #
+ # @param [String] filename Local path of the file to stream
+ # @param [Hash] args Options
+ #
+ # @return [EventMachine::Deferrable]
+ def stream_file_data filename, args={}
+ EventMachine::FileStreamer.new( self, filename, args )
+ end
+
+ # Watches connection for readability. Only possible if the connection was created
+ # using {EventMachine.attach} and had {EventMachine.notify_readable}/{EventMachine.notify_writable} defined on the handler.
+ #
+ # @see #notify_readable?
+ def notify_readable= mode
+ EventMachine::set_notify_readable @signature, mode
+ end
+
+ # @return [Boolean] true if the connection is being watched for readability.
+ def notify_readable?
+ EventMachine::is_notify_readable @signature
+ end
+
+ # Watches connection for writeability. Only possible if the connection was created
+ # using {EventMachine.attach} and had {EventMachine.notify_readable}/{EventMachine.notify_writable} defined on the handler.
+ #
+ # @see #notify_writable?
+ def notify_writable= mode
+ EventMachine::set_notify_writable @signature, mode
+ end
+
+ # Returns true if the connection is being watched for writability.
+ def notify_writable?
+ EventMachine::is_notify_writable @signature
+ end
+
+ # Pause a connection so that {#send_data} and {#receive_data} events are not fired until {#resume} is called.
+ # @see #resume
+ def pause
+ EventMachine::pause_connection @signature
+ end
+
+ # Resume a connection's {#send_data} and {#receive_data} events.
+ # @see #pause
+ def resume
+ EventMachine::resume_connection @signature
+ end
+
+ # @return [Boolean] true if the connect was paused using {EventMachine::Connection#pause}.
+ # @see #pause
+ # @see #resume
+ def paused?
+ EventMachine::connection_paused? @signature
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable.rb
new file mode 100644
index 0000000..18a6d31
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable.rb
@@ -0,0 +1,210 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 Jul 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Deferrable
+ autoload :Pool, 'em/deferrable/pool'
+
+ # Specify a block to be executed if and when the Deferrable object receives
+ # a status of :succeeded. See #set_deferred_status for more information.
+ #
+ # Calling this method on a Deferrable object whose status is not yet known
+ # will cause the callback block to be stored on an internal list.
+ # If you call this method on a Deferrable whose status is :succeeded, the
+ # block will be executed immediately, receiving the parameters given to the
+ # prior #set_deferred_status call.
+ #
+ #--
+ # If there is no status, add a callback to an internal list.
+ # If status is succeeded, execute the callback immediately.
+ # If status is failed, do nothing.
+ #
+ def callback &block
+ return unless block
+ @deferred_status ||= :unknown
+ if @deferred_status == :succeeded
+ block.call(*@deferred_args)
+ elsif @deferred_status != :failed
+ @callbacks ||= []
+ @callbacks.unshift block # << block
+ end
+ self
+ end
+
+ # Cancels an outstanding callback to &block if any. Undoes the action of #callback.
+ #
+ def cancel_callback block
+ @callbacks ||= []
+ @callbacks.delete block
+ end
+
+ # Specify a block to be executed if and when the Deferrable object receives
+ # a status of :failed. See #set_deferred_status for more information.
+ #--
+ # If there is no status, add an errback to an internal list.
+ # If status is failed, execute the errback immediately.
+ # If status is succeeded, do nothing.
+ #
+ def errback &block
+ return unless block
+ @deferred_status ||= :unknown
+ if @deferred_status == :failed
+ block.call(*@deferred_args)
+ elsif @deferred_status != :succeeded
+ @errbacks ||= []
+ @errbacks.unshift block # << block
+ end
+ self
+ end
+
+ # Cancels an outstanding errback to &block if any. Undoes the action of #errback.
+ #
+ def cancel_errback block
+ @errbacks ||= []
+ @errbacks.delete block
+ end
+
+ # Sets the "disposition" (status) of the Deferrable object. See also the large set of
+ # sugarings for this method.
+ # Note that if you call this method without arguments,
+ # no arguments will be passed to the callback/errback.
+ # If the user has coded these with arguments, then the
+ # user code will throw an argument exception.
+ # Implementors of deferrable classes <b>must</b>
+ # document the arguments they will supply to user callbacks.
+ #
+ # OBSERVE SOMETHING VERY SPECIAL here: you may call this method even
+ # on the INSIDE of a callback. This is very useful when a previously-registered
+ # callback wants to change the parameters that will be passed to subsequently-registered
+ # ones.
+ #
+ # You may give either :succeeded or :failed as the status argument.
+ #
+ # If you pass :succeeded, then all of the blocks passed to the object using the #callback
+ # method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
+ # passed to the object using #errback will be discarded.
+ #
+ # If you pass :failed, then all of the blocks passed to the object using the #errback
+ # method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
+ # passed to the object using # callback will be discarded.
+ #
+ # If you pass any arguments to #set_deferred_status in addition to the status argument,
+ # they will be passed as arguments to any callbacks or errbacks that are executed.
+ # It's your responsibility to ensure that the argument lists specified in your callbacks and
+ # errbacks match the arguments given in calls to #set_deferred_status, otherwise Ruby will raise
+ # an ArgumentError.
+ #
+ #--
+ # We're shifting callbacks off and discarding them as we execute them.
+ # This is valid because by definition callbacks are executed no more than
+ # once. It also has the magic effect of permitting recursive calls, which
+ # means that a callback can call #set_deferred_status and change the parameters
+ # that will be sent to subsequent callbacks down the chain.
+ #
+ # Changed @callbacks and @errbacks from push/shift to unshift/pop, per suggestion
+ # by Kirk Haines, to work around the memory leak bug that still exists in many Ruby
+ # versions.
+ #
+ # Changed 15Sep07: after processing callbacks or errbacks, CLEAR the other set of
+ # handlers. This gets us a little closer to the behavior of Twisted's "deferred,"
+ # which only allows status to be set once. Prior to making this change, it was possible
+ # to "succeed" a Deferrable (triggering its callbacks), and then immediately "fail" it,
+ # triggering its errbacks! That is clearly undesirable, but it's just as undesirable
+ # to raise an exception is status is set more than once on a Deferrable. The latter
+ # behavior would invalidate the idiom of resetting arguments by setting status from
+ # within a callback or errback, but more seriously it would cause spurious errors
+ # if a Deferrable was timed out and then an attempt was made to succeed it. See the
+ # comments under the new method #timeout.
+ #
+ def set_deferred_status status, *args
+ cancel_timeout
+ @errbacks ||= nil
+ @callbacks ||= nil
+ @deferred_status = status
+ @deferred_args = args
+ case @deferred_status
+ when :succeeded
+ if @callbacks
+ while cb = @callbacks.pop
+ cb.call(*@deferred_args)
+ end
+ end
+ @errbacks.clear if @errbacks
+ when :failed
+ if @errbacks
+ while eb = @errbacks.pop
+ eb.call(*@deferred_args)
+ end
+ end
+ @callbacks.clear if @callbacks
+ end
+ end
+
+
+ # Setting a timeout on a Deferrable causes it to go into the failed state after
+ # the Timeout expires (passing no arguments to the object's errbacks).
+ # Setting the status at any time prior to a call to the expiration of the timeout
+ # will cause the timer to be cancelled.
+ def timeout seconds, *args
+ cancel_timeout
+ me = self
+ @deferred_timeout = EventMachine::Timer.new(seconds) {me.fail(*args)}
+ self
+ end
+
+ # Cancels an outstanding timeout if any. Undoes the action of #timeout.
+ #
+ def cancel_timeout
+ @deferred_timeout ||= nil
+ if @deferred_timeout
+ @deferred_timeout.cancel
+ @deferred_timeout = nil
+ end
+ end
+
+
+ # Sugar for set_deferred_status(:succeeded, ...)
+ #
+ def succeed *args
+ set_deferred_status :succeeded, *args
+ end
+ alias set_deferred_success succeed
+
+ # Sugar for set_deferred_status(:failed, ...)
+ #
+ def fail *args
+ set_deferred_status :failed, *args
+ end
+ alias set_deferred_failure fail
+ end
+
+
+ # DefaultDeferrable is an otherwise empty class that includes Deferrable.
+ # This is very useful when you just need to return a Deferrable object
+ # as a way of communicating deferred status to some other part of a program.
+ class DefaultDeferrable
+ include Deferrable
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable/pool.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable/pool.rb
new file mode 100644
index 0000000..3c278ee
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/deferrable/pool.rb
@@ -0,0 +1,2 @@
+warn "EM::Deferrable::Pool is deprecated, please use EM::Pool"
+EM::Deferrable::Pool = EM::Pool \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/file_watch.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/file_watch.rb
new file mode 100644
index 0000000..074ffed
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/file_watch.rb
@@ -0,0 +1,73 @@
+module EventMachine
+ # Utility class that is useful for file monitoring. Supported events are
+ #
+ # * File is modified
+ # * File is deleted
+ # * File is moved
+ #
+ # @note On Mac OS X, file watching only works when kqueue is enabled
+ #
+ # @see EventMachine.watch_file
+ class FileWatch < Connection
+ # @private
+ Cmodified = 'modified'.freeze
+ # @private
+ Cdeleted = 'deleted'.freeze
+ # @private
+ Cmoved = 'moved'.freeze
+
+
+ # @private
+ def receive_data(data)
+ case data
+ when Cmodified
+ file_modified
+ when Cdeleted
+ file_deleted
+ when Cmoved
+ file_moved
+ end
+ end
+
+ # Returns the path that is being monitored.
+ #
+ # @note Current implementation does not pick up on the new filename after a rename occurs.
+ #
+ # @return [String]
+ # @see EventMachine.watch_file
+ def path
+ @path
+ end
+
+ # Will be called when the file is modified. Supposed to be redefined by subclasses.
+ #
+ # @abstract
+ def file_modified
+ end
+
+ # Will be called when the file is deleted. Supposed to be redefined by subclasses.
+ # When the file is deleted, stop_watching will be called after this to make sure everything is
+ # cleaned up correctly.
+ #
+ # @note On Linux (with {http://en.wikipedia.org/wiki/Inotify inotify}), this method will not be called until *all* open file descriptors to
+ # the file have been closed.
+ #
+ # @abstract
+ def file_deleted
+ end
+
+ # Will be called when the file is moved or renamed. Supposed to be redefined by subclasses.
+ #
+ # @abstract
+ def file_moved
+ end
+
+ # Discontinue monitoring of the file.
+ #
+ # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing {EventMachine::Connection#unbind}.
+ # This will be called automatically when a file is deleted. User code may call it as well.
+ def stop_watching
+ EventMachine::unwatch_filename(@signature)
+ end # stop_watching
+ end # FileWatch
+end # EventMachine
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/future.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/future.rb
new file mode 100644
index 0000000..4affbf5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/future.rb
@@ -0,0 +1,61 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 Jul 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+#--
+# This defines EventMachine::Deferrable#future, which requires
+# that the rest of EventMachine::Deferrable has already been seen.
+# (It's in deferrable.rb.)
+
+module EventMachine
+ module Deferrable
+
+ # A future is a sugaring of a typical deferrable usage.
+ #--
+ # Evaluate arg (which may be an expression or a block).
+ # What's the class of arg?
+ # If arg is an ordinary expression, then return it.
+ # If arg is deferrable (responds to :set_deferred_status),
+ # then look at the arguments. If either callback or errback
+ # are defined, then use them. If neither are defined, then
+ # use the supplied block (if any) as the callback.
+ # Then return arg.
+ def self.future arg, cb=nil, eb=nil, &blk
+ arg = arg.call if arg.respond_to?(:call)
+
+ if arg.respond_to?(:set_deferred_status)
+ if cb || eb
+ arg.callback(&cb) if cb
+ arg.errback(&eb) if eb
+ else
+ arg.callback(&blk) if blk
+ end
+ end
+
+ arg
+ end
+
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/iterator.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/iterator.rb
new file mode 100644
index 0000000..a30b9dd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/iterator.rb
@@ -0,0 +1,252 @@
+module EventMachine
+ # A simple iterator for concurrent asynchronous work.
+ #
+ # Unlike ruby's built-in iterators, the end of the current iteration cycle is signaled manually,
+ # instead of happening automatically after the yielded block finishes executing. For example:
+ #
+ # (0..10).each{ |num| }
+ #
+ # becomes:
+ #
+ # EM::Iterator.new(0..10).each{ |num,iter| iter.next }
+ #
+ # This is especially useful when doing asynchronous work via reactor libraries and
+ # functions. For example, given a sync and async http api:
+ #
+ # response = sync_http_get(url); ...
+ # async_http_get(url){ |response| ... }
+ #
+ # a synchronous iterator such as:
+ #
+ # responses = urls.map{ |url| sync_http_get(url) }
+ # ...
+ # puts 'all done!'
+ #
+ # could be written as:
+ #
+ # EM::Iterator.new(urls).map(proc{ |url,iter|
+ # async_http_get(url){ |res|
+ # iter.return(res)
+ # }
+ # }, proc{ |responses|
+ # ...
+ # puts 'all done!'
+ # })
+ #
+ # Now, you can take advantage of the asynchronous api to issue requests in parallel. For example,
+ # to fetch 10 urls at a time, simply pass in a concurrency of 10:
+ #
+ # EM::Iterator.new(urls, 10).each do |url,iter|
+ # async_http_get(url){ iter.next }
+ # end
+ #
+ class Iterator
+ Stop = "EM::Stop"
+ # Create a new parallel async iterator with specified concurrency.
+ #
+ # i = EM::Iterator.new(1..100, 10)
+ #
+ # will create an iterator over the range that processes 10 items at a time. Iteration
+ # is started via #each, #map or #inject
+ #
+ # The list may either be an array-like object, or a proc that returns a new object
+ # to be processed each time it is called. If a proc is used, it must return
+ # EventMachine::Iterator::Stop to signal the end of the iterations.
+ #
+ def initialize(list, concurrency = 1)
+ raise ArgumentError, 'concurrency must be bigger than zero' unless (concurrency > 0)
+ if list.respond_to?(:call)
+ @list = nil
+ @list_proc = list
+ elsif list.respond_to?(:to_a)
+ @list = list.to_a.dup
+ @list_proc = nil
+ else
+ raise ArgumentError, 'argument must be a proc or an array'
+ end
+ @concurrency = concurrency
+
+ @started = false
+ @ended = false
+ end
+
+ # Change the concurrency of this iterator. Workers will automatically be spawned or destroyed
+ # to accomodate the new concurrency level.
+ #
+ def concurrency=(val)
+ old = @concurrency
+ @concurrency = val
+
+ spawn_workers if val > old and @started and !@ended
+ end
+ attr_reader :concurrency
+
+ # Iterate over a set of items using the specified block or proc.
+ #
+ # EM::Iterator.new(1..100).each do |num, iter|
+ # puts num
+ # iter.next
+ # end
+ #
+ # An optional second proc is invoked after the iteration is complete.
+ #
+ # EM::Iterator.new(1..100).each(
+ # proc{ |num,iter| iter.next },
+ # proc{ puts 'all done' }
+ # )
+ #
+ def each(foreach=nil, after=nil, &blk)
+ raise ArgumentError, 'proc or block required for iteration' unless foreach ||= blk
+ raise RuntimeError, 'cannot iterate over an iterator more than once' if @started or @ended
+
+ @started = true
+ @pending = 0
+ @workers = 0
+
+ all_done = proc{
+ after.call if after and @ended and @pending == 0
+ }
+
+ @process_next = proc{
+ # p [:process_next, :pending=, @pending, :workers=, @workers, :ended=, @ended, :concurrency=, @concurrency, :list=, @list]
+ unless @ended or @workers > @concurrency
+ item = next_item()
+ if item.equal?(Stop)
+ @ended = true
+ @workers -= 1
+ all_done.call
+ else
+ @pending += 1
+
+ is_done = false
+ on_done = proc{
+ raise RuntimeError, 'already completed this iteration' if is_done
+ is_done = true
+
+ @pending -= 1
+
+ if @ended
+ all_done.call
+ else
+ EM.next_tick(@process_next)
+ end
+ }
+ class << on_done
+ alias :next :call
+ end
+
+ foreach.call(item, on_done)
+ end
+ else
+ @workers -= 1
+ end
+ }
+
+ spawn_workers
+
+ self
+ end
+
+ # Collect the results of an asynchronous iteration into an array.
+ #
+ # EM::Iterator.new(%w[ pwd uptime uname date ], 2).map(proc{ |cmd,iter|
+ # EM.system(cmd){ |output,status|
+ # iter.return(output)
+ # }
+ # }, proc{ |results|
+ # p results
+ # })
+ #
+ def map(foreach, after)
+ index = 0
+
+ inject([], proc{ |results,item,iter|
+ i = index
+ index += 1
+
+ is_done = false
+ on_done = proc{ |res|
+ raise RuntimeError, 'already returned a value for this iteration' if is_done
+ is_done = true
+
+ results[i] = res
+ iter.return(results)
+ }
+ class << on_done
+ alias :return :call
+ def next
+ raise NoMethodError, 'must call #return on a map iterator'
+ end
+ end
+
+ foreach.call(item, on_done)
+ }, proc{ |results|
+ after.call(results)
+ })
+ end
+
+ # Inject the results of an asynchronous iteration onto a given object.
+ #
+ # EM::Iterator.new(%w[ pwd uptime uname date ], 2).inject({}, proc{ |hash,cmd,iter|
+ # EM.system(cmd){ |output,status|
+ # hash[cmd] = status.exitstatus == 0 ? output.strip : nil
+ # iter.return(hash)
+ # }
+ # }, proc{ |results|
+ # p results
+ # })
+ #
+ def inject(obj, foreach, after)
+ each(proc{ |item,iter|
+ is_done = false
+ on_done = proc{ |res|
+ raise RuntimeError, 'already returned a value for this iteration' if is_done
+ is_done = true
+
+ obj = res
+ iter.next
+ }
+ class << on_done
+ alias :return :call
+ def next
+ raise NoMethodError, 'must call #return on an inject iterator'
+ end
+ end
+
+ foreach.call(obj, item, on_done)
+ }, proc{
+ after.call(obj)
+ })
+ end
+
+ private
+
+ # Spawn workers to consume items from the iterator's enumerator based on the current concurrency level.
+ #
+ def spawn_workers
+ EM.next_tick(start_worker = proc{
+ if @workers < @concurrency and !@ended
+ # p [:spawning_worker, :workers=, @workers, :concurrency=, @concurrency, :ended=, @ended]
+ @workers += 1
+ @process_next.call
+ EM.next_tick(start_worker)
+ end
+ })
+ nil
+ end
+
+ # Return the next item from @list or @list_proc.
+ # Once items have run out, will return EM::Iterator::Stop. Procs must supply this themselves
+ def next_item
+ if @list_proc
+ @list_proc.call
+ else
+ @list.empty? ? Stop : @list.shift
+ end
+ end
+ end
+end
+
+# TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next }
+# TODO: support iter.pause/resume/stop/break/continue?
+# TODO: create some exceptions instead of using RuntimeError
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/messages.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/messages.rb
new file mode 100644
index 0000000..9a51c39
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/messages.rb
@@ -0,0 +1,66 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 Jul 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+=begin
+
+Message Routing in EventMachine.
+
+The goal here is to enable "routing points," objects that can send and receive
+"messages," which are delimited streams of bytes. The boundaries of a message
+are preserved as it passes through the reactor system.
+
+There will be several module methods defined in EventMachine to create route-point
+objects (which will probably have a base class of EventMachine::MessageRouter
+until someone suggests a better name).
+
+As with I/O objects, routing objects will receive events by having the router
+core call methods on them. And of course user code can and will define handlers
+to deal with events of interest.
+
+The message router base class only really needs a receive_message method. There will
+be an EM module-method to send messages, in addition to the module methods to create
+the various kinds of message receivers.
+
+The simplest kind of message receiver object can receive messages by being named
+explicitly in a parameter to EM#send_message. More sophisticated receivers can define
+pub-sub selectors and message-queue names. And they can also define channels for
+route-points in other processes or even on other machines.
+
+A message is NOT a marshallable entity. Rather, it's a chunk of flat content more like
+an Erlang message. Initially, all content submitted for transmission as a message will
+have the to_s method called on it. Eventually, we'll be able to transmit certain structured
+data types (XML and YAML documents, Structs within limits) and have them reconstructed
+on the other end.
+
+A fundamental goal of the message-routing capability is to interoperate seamlessly with
+external systems, including non-Ruby systems like ActiveMQ. We will define various protocol
+handlers for things like Stomp and possibly AMQP, but these will be wrapped up and hidden
+from the users of the basic routing capability.
+
+As with Erlang, a critical goal is for programs that are built to use message-passing to work
+WITHOUT CHANGE when the code is re-based on a multi-process system.
+
+=end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pool.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pool.rb
new file mode 100644
index 0000000..2cb3662
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pool.rb
@@ -0,0 +1,151 @@
+module EventMachine
+ # A simple async resource pool based on a resource and work queue. Resources
+ # are enqueued and work waits for resources to become available.
+ #
+ # @example
+ # require 'em-http-request'
+ #
+ # EM.run do
+ # pool = EM::Pool.new
+ # spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') }
+ # 10.times { spawn[] }
+ # done, scheduled = 0, 0
+ #
+ # check = lambda do
+ # done += 1
+ # if done >= scheduled
+ # EM.stop
+ # end
+ # end
+ #
+ # pool.on_error { |conn| spawn[] }
+ #
+ # 100.times do |i|
+ # scheduled += 1
+ # pool.perform do |conn|
+ # req = conn.get :path => '/', :keepalive => true
+ #
+ # req.callback do
+ # p [:success, conn.object_id, i, req.response.size]
+ # check[]
+ # end
+ #
+ # req.errback { check[] }
+ #
+ # req
+ # end
+ # end
+ # end
+ #
+ # Resources are expected to be controlled by an object responding to a
+ # deferrable/completion style API with callback and errback blocks.
+ #
+ class Pool
+
+ def initialize
+ @resources = EM::Queue.new
+ @removed = []
+ @contents = []
+ @on_error = nil
+ end
+
+ def add resource
+ @contents << resource
+ requeue resource
+ end
+
+ def remove resource
+ @contents.delete resource
+ @removed << resource
+ end
+
+ # Returns a list for introspection purposes only. You should *NEVER* call
+ # modification or work oriented methods on objects in this list. A good
+ # example use case is periodic statistics collection against a set of
+ # connection resources.
+ #
+ # @example
+ # pool.contents.inject(0) { |sum, connection| connection.num_bytes }
+ def contents
+ @contents.dup
+ end
+
+ # Define a default catch-all for when the deferrables returned by work
+ # blocks enter a failed state. By default all that happens is that the
+ # resource is returned to the pool. If on_error is defined, this block is
+ # responsible for re-adding the resource to the pool if it is still usable.
+ # In other words, it is generally assumed that on_error blocks explicitly
+ # handle the rest of the lifetime of the resource.
+ def on_error *a, &b
+ @on_error = EM::Callback(*a, &b)
+ end
+
+ # Perform a given #call-able object or block. The callable object will be
+ # called with a resource from the pool as soon as one is available, and is
+ # expected to return a deferrable.
+ #
+ # The deferrable will have callback and errback added such that when the
+ # deferrable enters a finished state, the object is returned to the pool.
+ #
+ # If on_error is defined, then objects are not automatically returned to the
+ # pool.
+ def perform(*a, &b)
+ work = EM::Callback(*a, &b)
+
+ @resources.pop do |resource|
+ if removed? resource
+ @removed.delete resource
+ reschedule work
+ else
+ process work, resource
+ end
+ end
+ end
+ alias reschedule perform
+
+ # A peek at the number of enqueued jobs waiting for resources
+ def num_waiting
+ @resources.num_waiting
+ end
+
+ # Removed will show resources in a partial pruned state. Resources in the
+ # removed list may not appear in the contents list if they are currently in
+ # use.
+ def removed? resource
+ @removed.include? resource
+ end
+
+ protected
+ def requeue resource
+ @resources.push resource
+ end
+
+ def failure resource
+ if @on_error
+ @contents.delete resource
+ @on_error.call resource
+ # Prevent users from calling a leak.
+ @removed.delete resource
+ else
+ requeue resource
+ end
+ end
+
+ def completion deferrable, resource
+ deferrable.callback { requeue resource }
+ deferrable.errback { failure resource }
+ end
+
+ def process work, resource
+ deferrable = work.call resource
+ if deferrable.kind_of?(EM::Deferrable)
+ completion deferrable, resource
+ else
+ raise ArgumentError, "deferrable expected from work"
+ end
+ rescue
+ failure resource
+ raise
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/process_watch.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/process_watch.rb
new file mode 100644
index 0000000..66e8943
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/process_watch.rb
@@ -0,0 +1,45 @@
+module EventMachine
+
+ # This is subclassed from EventMachine::Connection for use with the process monitoring API. Read the
+ # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_process.
+ class ProcessWatch < Connection
+ # @private
+ Cfork = 'fork'.freeze
+ # @private
+ Cexit = 'exit'.freeze
+
+ # @private
+ def receive_data(data)
+ case data
+ when Cfork
+ process_forked
+ when Cexit
+ process_exited
+ end
+ end
+
+ # Returns the pid that EventMachine::watch_process was originally called with.
+ def pid
+ @pid
+ end
+
+ # Should be redefined with the user's custom callback that will be fired when the prcess is forked.
+ #
+ # There is currently not an easy way to get the pid of the forked child.
+ def process_forked
+ end
+
+ # Should be redefined with the user's custom callback that will be fired when the process exits.
+ #
+ # stop_watching is called automatically after this callback
+ def process_exited
+ end
+
+ # Discontinue monitoring of the process.
+ # This will be called automatically when a process dies. User code may call it as well.
+ def stop_watching
+ EventMachine::unwatch_pid(@signature)
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/processes.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/processes.rb
new file mode 100644
index 0000000..4bbc14f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/processes.rb
@@ -0,0 +1,123 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 13 Dec 07
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+
+module EventMachine
+
+ # EM::DeferrableChildProcess is a sugaring of a common use-case
+ # involving EM::popen.
+ # Call the #open method on EM::DeferrableChildProcess, passing
+ # a command-string. #open immediately returns an EM::Deferrable
+ # object. It also schedules the forking of a child process, which
+ # will execute the command passed to #open.
+ # When the forked child terminates, the Deferrable will be signalled
+ # and execute its callbacks, passing the data that the child process
+ # wrote to stdout.
+ #
+ class DeferrableChildProcess < EventMachine::Connection
+ include EventMachine::Deferrable
+
+ # @private
+ def initialize
+ super
+ @data = []
+ end
+
+ # Sugars a common use-case involving forked child processes.
+ # #open takes a String argument containing an shell command
+ # string (including arguments if desired). #open immediately
+ # returns an EventMachine::Deferrable object, without blocking.
+ #
+ # It also invokes EventMachine#popen to run the passed-in
+ # command in a forked child process.
+ #
+ # When the forked child terminates, the Deferrable that
+ # #open calls its callbacks, passing the data returned
+ # from the child process.
+ #
+ def self.open cmd
+ EventMachine.popen( cmd, DeferrableChildProcess )
+ end
+
+ # @private
+ def receive_data data
+ @data << data
+ end
+
+ # @private
+ def unbind
+ succeed( @data.join )
+ end
+ end
+
+ # @private
+ class SystemCmd < EventMachine::Connection
+ def initialize cb
+ @cb = cb
+ @output = []
+ end
+ def receive_data data
+ @output << data
+ end
+ def unbind
+ @cb.call @output.join(''), get_status if @cb
+ end
+ end
+
+ # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a
+ # single string argument for the command and performs no shell expansion.
+ #
+ # The block or proc passed to EM::system is called with two arguments: the output generated by the command,
+ # and a Process::Status that contains information about the command's execution.
+ #
+ # EM.run{
+ # EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 }
+ # }
+ #
+ # You can also supply an additional proc to send some data to the process:
+ #
+ # EM.run{
+ # EM.system('sh', proc{ |process|
+ # process.send_data("echo hello\n")
+ # process.send_data("exit\n")
+ # }, proc{ |out,status|
+ # puts(out)
+ # })
+ # }
+ #
+ # Like EventMachine.popen, EventMachine.system currently does not work on windows.
+ # It returns the pid of the spawned process.
+ def EventMachine::system cmd, *args, &cb
+ cb ||= args.pop if args.last.is_a? Proc
+ init = args.pop if args.last.is_a? Proc
+
+ # merge remaining arguments into the command
+ cmd = [cmd, *args] if args.any?
+
+ EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c|
+ init[c] if init
+ end.signature)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols.rb
new file mode 100644
index 0000000..0c17906
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols.rb
@@ -0,0 +1,37 @@
+module EventMachine
+ # This module contains various protocol implementations, including:
+ # - HttpClient and HttpClient2
+ # - Stomp
+ # - Memcache
+ # - SmtpClient and SmtpServer
+ # - SASLauth and SASLauthclient
+ # - LineProtocol, LineAndTextProtocol and LineText2
+ # - HeaderAndContentProtocol
+ # - Postgres3
+ # - ObjectProtocol
+ #
+ # The protocol implementations live in separate files in the protocols/ subdirectory,
+ # but are auto-loaded when they are first referenced in your application.
+ #
+ # EventMachine::Protocols is also aliased to EM::P for easier usage.
+ #
+ module Protocols
+ # TODO : various autotools are completely useless with the lack of naming
+ # convention, we need to correct that!
+ autoload :TcpConnectTester, 'em/protocols/tcptest'
+ autoload :HttpClient, 'em/protocols/httpclient'
+ autoload :HttpClient2, 'em/protocols/httpclient2'
+ autoload :LineAndTextProtocol, 'em/protocols/line_and_text'
+ autoload :HeaderAndContentProtocol, 'em/protocols/header_and_content'
+ autoload :LineText2, 'em/protocols/linetext2'
+ autoload :Stomp, 'em/protocols/stomp'
+ autoload :SmtpClient, 'em/protocols/smtpclient'
+ autoload :SmtpServer, 'em/protocols/smtpserver'
+ autoload :SASLauth, 'em/protocols/saslauth'
+ autoload :Memcache, 'em/protocols/memcache'
+ autoload :Postgres3, 'em/protocols/postgres3'
+ autoload :ObjectProtocol, 'em/protocols/object_protocol'
+ autoload :Socks4, 'em/protocols/socks4'
+ autoload :LineProtocol, 'em/protocols/line_protocol'
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb
new file mode 100644
index 0000000..b5a465a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/header_and_content.rb
@@ -0,0 +1,138 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 Nov 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # === Usage
+ #
+ # class RequestHandler < EM::P::HeaderAndContentProtocol
+ # def receive_request headers, content
+ # p [:request, headers, content]
+ # end
+ # end
+ #
+ # EM.run{
+ # EM.start_server 'localhost', 80, RequestHandler
+ # }
+ #
+ #--
+ # Originally, this subclassed LineAndTextProtocol, which in
+ # turn relies on BufferedTokenizer, which doesn't gracefully
+ # handle the transitions between lines and binary text.
+ # Changed 13Sep08 by FCianfrocca.
+ class HeaderAndContentProtocol < Connection
+ include LineText2
+
+ ContentLengthPattern = /Content-length:\s*(\d+)/i
+
+ def initialize *args
+ super
+ init_for_request
+ end
+
+ def receive_line line
+ case @hc_mode
+ when :discard_blanks
+ unless line == ""
+ @hc_mode = :headers
+ receive_line line
+ end
+ when :headers
+ if line == ""
+ raise "unrecognized state" unless @hc_headers.length > 0
+ if respond_to?(:receive_headers)
+ receive_headers @hc_headers
+ end
+ # @hc_content_length will be nil, not 0, if there was no content-length header.
+ if @hc_content_length.to_i > 0
+ set_binary_mode @hc_content_length
+ else
+ dispatch_request
+ end
+ else
+ @hc_headers << line
+ if ContentLengthPattern =~ line
+ # There are some attacks that rely on sending multiple content-length
+ # headers. This is a crude protection, but needs to become tunable.
+ raise "extraneous content-length header" if @hc_content_length
+ @hc_content_length = $1.to_i
+ end
+ if @hc_headers.length == 1 and respond_to?(:receive_first_header_line)
+ receive_first_header_line line
+ end
+ end
+ else
+ raise "internal error, unsupported mode"
+ end
+ end
+
+ def receive_binary_data text
+ @hc_content = text
+ dispatch_request
+ end
+
+ def dispatch_request
+ if respond_to?(:receive_request)
+ receive_request @hc_headers, @hc_content
+ end
+ init_for_request
+ end
+ private :dispatch_request
+
+ def init_for_request
+ @hc_mode = :discard_blanks
+ @hc_headers = []
+ # originally was @hc_headers ||= []; @hc_headers.clear to get a performance
+ # boost, but it's counterproductive because a subclassed handler will have to
+ # call dup to use the header array we pass in receive_headers.
+
+ @hc_content_length = nil
+ @hc_content = ""
+ end
+ private :init_for_request
+
+ # Basically a convenience method. We might create a subclass that does this
+ # automatically. But it's such a performance killer.
+ def headers_2_hash hdrs
+ self.class.headers_2_hash hdrs
+ end
+
+ class << self
+ def headers_2_hash hdrs
+ hash = {}
+ hdrs.each {|h|
+ if /\A([^\s:]+)\s*:\s*/ =~ h
+ tail = $'.dup
+ hash[ $1.downcase.gsub(/-/,"_").intern ] = tail
+ end
+ }
+ hash
+ end
+ end
+
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb
new file mode 100644
index 0000000..38b175c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient.rb
@@ -0,0 +1,300 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
+ #
+ # @example
+ # EventMachine.run {
+ # http = EventMachine::Protocols::HttpClient.request(
+ # :host => server,
+ # :port => 80,
+ # :request => "/index.html",
+ # :query_string => "parm1=value1&parm2=value2"
+ # )
+ # http.callback {|response|
+ # puts response[:status]
+ # puts response[:headers]
+ # puts response[:content]
+ # }
+ # }
+ #--
+ # TODO:
+ # Add streaming so we can support enormous POSTs. Current max is 20meg.
+ # Timeout for connections that run too long or hang somewhere in the middle.
+ # Persistent connections (HTTP/1.1), may need a associated delegate object.
+ # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
+ # DNS lookups are unbelievably slow.
+ # HEAD requests.
+ # Convenience methods for requests. get, post, url, etc.
+ # SSL.
+ # Handle status codes like 304, 100, etc.
+ # Refactor this code so that protocol errors all get handled one way (an exception?),
+ # instead of sprinkling set_deferred_status :failed calls everywhere.
+ class HttpClient < Connection
+ include EventMachine::Deferrable
+
+ MaxPostContentLength = 20 * 1024 * 1024
+
+ def initialize
+ warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead."
+ @connected = false
+ end
+
+ # @param args [Hash] The request arguments
+ # @option args [String] :host The host IP/DNS name
+ # @option args [Integer] :port The port to connect too
+ # @option args [String] :verb The request type [GET | POST | DELETE | PUT]
+ # @option args [String] :request The request path
+ # @option args [Hash] :basic_auth The basic auth credentials (:username and :password)
+ # @option args [String] :content The request content
+ # @option args [String] :contenttype The content type (e.g. text/plain)
+ # @option args [String] :query_string The query string
+ # @option args [String] :host_header The host header to set
+ # @option args [String] :cookie Cookies to set
+ def self.request( args = {} )
+ args[:port] ||= 80
+ EventMachine.connect( args[:host], args[:port], self ) {|c|
+ # According to the docs, we will get here AFTER post_init is called.
+ c.instance_eval {@args = args}
+ }
+ end
+
+ def post_init
+ @start_time = Time.now
+ @data = ""
+ @read_state = :base
+ end
+
+ # We send the request when we get a connection.
+ # AND, we set an instance variable to indicate we passed through here.
+ # That allows #unbind to know whether there was a successful connection.
+ # NB: This naive technique won't work when we have to support multiple
+ # requests on a single connection.
+ def connection_completed
+ @connected = true
+ send_request @args
+ end
+
+ def send_request args
+ args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
+ args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
+
+ verb = args[:verb].to_s.upcase
+ unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
+ set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
+ return # NOTE THE EARLY RETURN, we're not sending any data.
+ end
+
+ request = args[:request] || "/"
+ unless request[0,1] == "/"
+ request = "/" + request
+ end
+
+ qs = args[:query_string] || ""
+ if qs.length > 0 and qs[0,1] != '?'
+ qs = "?" + qs
+ end
+
+ version = args[:version] || "1.1"
+
+ # Allow an override for the host header if it's not the connect-string.
+ host = args[:host_header] || args[:host] || "_"
+ # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
+ port = args[:port].to_i != 80 ? ":#{args[:port]}" : ""
+
+ # POST items.
+ postcontenttype = args[:contenttype] || "application/octet-stream"
+ postcontent = args[:content] || ""
+ raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
+
+ # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
+ # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
+ req = [
+ "#{verb} #{request}#{qs} HTTP/#{version}",
+ "Host: #{host}#{port}",
+ "User-agent: Ruby EventMachine",
+ ]
+
+ if verb == "POST" || verb == "PUT"
+ req << "Content-type: #{postcontenttype}"
+ req << "Content-length: #{postcontent.length}"
+ end
+
+ # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
+ # Eventually we will want to deal intelligently with arrays and hashes.
+ if args[:cookie]
+ req << "Cookie: #{args[:cookie]}"
+ end
+
+ # Allow custom HTTP headers, e.g. SOAPAction
+ args[:custom_headers].each do |k,v|
+ req << "#{k}: #{v}"
+ end if args[:custom_headers]
+
+ # Basic-auth stanza contributed by Matt Murphy.
+ if args[:basic_auth]
+ basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
+ req << "Authorization: Basic #{basic_auth_string}"
+ end
+
+ req << ""
+ reqstring = req.map {|l| "#{l}\r\n"}.join
+ send_data reqstring
+
+ if verb == "POST" || verb == "PUT"
+ send_data postcontent
+ end
+ end
+
+
+ def receive_data data
+ while data and data.length > 0
+ case @read_state
+ when :base
+ # Perform any per-request initialization here and don't consume any data.
+ @data = ""
+ @headers = []
+ @content_length = nil # not zero
+ @content = ""
+ @status = nil
+ @chunked = false
+ @chunk_length = nil
+ @read_state = :header
+ @connection_close = nil
+ when :header
+ ary = data.split( /\r?\n/m, 2 )
+ if ary.length == 2
+ data = ary.last
+ if ary.first == ""
+ if (@content_length and @content_length > 0) || @chunked || @connection_close
+ @read_state = :content
+ else
+ dispatch_response
+ @read_state = :base
+ end
+ else
+ @headers << ary.first
+ if @headers.length == 1
+ parse_response_line
+ elsif ary.first =~ /\Acontent-length:\s*/i
+ # Only take the FIRST content-length header that appears,
+ # which we can distinguish because @content_length is nil.
+ # TODO, it's actually a fatal error if there is more than one
+ # content-length header, because the caller is presumptively
+ # a bad guy. (There is an exploit that depends on multiple
+ # content-length headers.)
+ @content_length ||= $'.to_i
+ elsif ary.first =~ /\Aconnection:\s*close/i
+ @connection_close = true
+ elsif ary.first =~ /\Atransfer-encoding:\s*chunked/i
+ @chunked = true
+ end
+ end
+ else
+ @data << data
+ data = ""
+ end
+ when :content
+ if @chunked && @chunk_length
+ bytes_needed = @chunk_length - @chunk_read
+ new_data = data[0, bytes_needed]
+ @chunk_read += new_data.length
+ @content += new_data
+ data = data[bytes_needed..-1] || ""
+ if @chunk_length == @chunk_read && data[0,2] == "\r\n"
+ @chunk_length = nil
+ data = data[2..-1]
+ end
+ elsif @chunked
+ if (m = data.match(/\A(\S*)\r\n/m))
+ data = data[m[0].length..-1]
+ @chunk_length = m[1].to_i(16)
+ @chunk_read = 0
+ if @chunk_length == 0
+ dispatch_response
+ @read_state = :base
+ end
+ end
+ elsif @content_length
+ # If there was no content-length header, we have to wait until the connection
+ # closes. Everything we get until that point is content.
+ # TODO: Must impose a content-size limit, and also must implement chunking.
+ # Also, must support either temporary files for large content, or calling
+ # a content-consumer block supplied by the user.
+ bytes_needed = @content_length - @content.length
+ @content += data[0, bytes_needed]
+ data = data[bytes_needed..-1] || ""
+ if @content_length == @content.length
+ dispatch_response
+ @read_state = :base
+ end
+ else
+ @content << data
+ data = ""
+ end
+ end
+ end
+ end
+
+
+ # We get called here when we have received an HTTP response line.
+ # It's an opportunity to throw an exception or trigger other exceptional
+ # handling.
+ def parse_response_line
+ if @headers.first =~ /\AHTTP\/1\.[01] ([\d]{3})/
+ @status = $1.to_i
+ else
+ set_deferred_status :failed, {
+ :status => 0 # crappy way of signifying an unrecognized response. TODO, find a better way to do this.
+ }
+ close_connection
+ end
+ end
+ private :parse_response_line
+
+ def dispatch_response
+ @read_state = :base
+ set_deferred_status :succeeded, {
+ :content => @content,
+ :headers => @headers,
+ :status => @status
+ }
+ # TODO, we close the connection for now, but this is wrong for persistent clients.
+ close_connection
+ end
+
+ def unbind
+ if !@connected
+ set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
+ elsif (@read_state == :content and @content_length == nil)
+ dispatch_response
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb
new file mode 100644
index 0000000..0fb64e8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/httpclient2.rb
@@ -0,0 +1,600 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # <b>Note:</b> This class is deprecated and will be removed. Please use EM-HTTP-Request instead.
+ #
+ # === Usage
+ #
+ # EM.run{
+ # conn = EM::Protocols::HttpClient2.connect 'google.com', 80
+ #
+ # req = conn.get('/')
+ # req.callback{ |response|
+ # p(response.status)
+ # p(response.headers)
+ # p(response.content)
+ # }
+ # }
+ class HttpClient2 < Connection
+ include LineText2
+
+ def initialize
+ warn "HttpClient2 is deprecated and will be removed. EM-Http-Request should be used instead."
+
+ @authorization = nil
+ @closed = nil
+ @requests = nil
+ end
+
+ # @private
+ class Request
+ include Deferrable
+
+ attr_reader :version
+ attr_reader :status
+ attr_reader :header_lines
+ attr_reader :headers
+ attr_reader :content
+ attr_reader :internal_error
+
+ def initialize conn, args
+ @conn = conn
+ @args = args
+ @header_lines = []
+ @headers = {}
+ @blanks = 0
+ @chunk_trailer = nil
+ @chunking = nil
+ end
+
+ def send_request
+ az = @args[:authorization] and az = "Authorization: #{az}\r\n"
+
+ r = [
+ "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
+ "Host: #{@args[:host_header] || "_"}\r\n",
+ az || "",
+ "\r\n"
+ ]
+ @conn.send_data r.join
+ end
+
+
+ #--
+ #
+ def receive_line ln
+ if @chunk_trailer
+ receive_chunk_trailer(ln)
+ elsif @chunking
+ receive_chunk_header(ln)
+ else
+ receive_header_line(ln)
+ end
+ end
+
+ #--
+ #
+ def receive_chunk_trailer ln
+ if ln.length == 0
+ @conn.pop_request
+ succeed(self)
+ else
+ p "Received chunk trailer line"
+ end
+ end
+
+ #--
+ # Allow up to ten blank lines before we get a real response line.
+ # Allow no more than 100 lines in the header.
+ #
+ def receive_header_line ln
+ if ln.length == 0
+ if @header_lines.length > 0
+ process_header
+ else
+ @blanks += 1
+ if @blanks > 10
+ @conn.close_connection
+ end
+ end
+ else
+ @header_lines << ln
+ if @header_lines.length > 100
+ @internal_error = :bad_header
+ @conn.close_connection
+ end
+ end
+ end
+
+ #--
+ # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
+ #
+ def receive_chunk_header ln
+ if ln.length > 0
+ chunksize = ln.to_i(16)
+ if chunksize > 0
+ @conn.set_text_mode(ln.to_i(16))
+ else
+ @content = @content ? @content.join : ''
+ @chunk_trailer = true
+ end
+ else
+ # We correctly come here after each chunk gets read.
+ # p "Got A BLANK chunk line"
+ end
+
+ end
+
+
+ #--
+ # We get a single chunk. Append it to the incoming content and switch back to line mode.
+ #
+ def receive_chunked_text text
+ # p "RECEIVED #{text.length} CHUNK"
+ (@content ||= []) << text
+ end
+
+
+ #--
+ # TODO, inefficient how we're handling this. Part of it is done so as to
+ # make sure we don't have problems in detecting chunked-encoding, content-length,
+ # etc.
+ #
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+ ClenRE = /\AContent-length:\s*(\d+)/i
+ ChunkedRE = /\ATransfer-encoding:\s*chunked/i
+ ColonRE = /\:\s*/
+
+ def process_header
+ unless @header_lines.first =~ HttpResponseRE
+ @conn.close_connection
+ @internal_error = :bad_request
+ end
+ @version = $1.dup
+ @status = $2.dup.to_i
+
+ clen = nil
+ chunks = nil
+ @header_lines.each_with_index do |e,ix|
+ if ix > 0
+ hdr,val = e.split(ColonRE,2)
+ (@headers[hdr.downcase] ||= []) << val
+ end
+
+ if clen == nil and e =~ ClenRE
+ clen = $1.dup.to_i
+ end
+ if e =~ ChunkedRE
+ chunks = true
+ end
+ end
+
+ if clen
+ # If the content length is zero we should not call set_text_mode,
+ # because a value of zero will make it wait forever, hanging the
+ # connection. Just return success instead, with empty content.
+ if clen == 0 then
+ @content = ""
+ @conn.pop_request
+ succeed(self)
+ else
+ @conn.set_text_mode clen
+ end
+ elsif chunks
+ @chunking = true
+ else
+ # Chunked transfer, multipart, or end-of-connection.
+ # For end-of-connection, we need to go the unbind
+ # method and suppress its desire to fail us.
+ p "NO CLEN"
+ p @args[:uri]
+ p @header_lines
+ @internal_error = :unsupported_clen
+ @conn.close_connection
+ end
+ end
+ private :process_header
+
+
+ def receive_text text
+ @chunking ? receive_chunked_text(text) : receive_sized_text(text)
+ end
+
+ #--
+ # At the present time, we only handle contents that have a length
+ # specified by the content-length header.
+ #
+ def receive_sized_text text
+ @content = text
+ @conn.pop_request
+ succeed(self)
+ end
+ end
+
+ # Make a connection to a remote HTTP server.
+ # Can take either a pair of arguments (which will be interpreted as
+ # a hostname/ip-address and a port), or a hash.
+ # If the arguments are a hash, then supported values include:
+ # :host => a hostname or ip-address
+ # :port => a port number
+ # :ssl => true to enable ssl
+ def self.connect *args
+ if args.length == 2
+ args = {:host=>args[0], :port=>args[1]}
+ else
+ args = args.first
+ end
+
+ h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
+ conn = EM.connect( h, prt, self )
+ conn.start_tls if ssl
+ conn.set_default_host_header( h, prt, ssl )
+ conn
+ end
+
+ # Get a url
+ #
+ # req = conn.get(:uri => '/')
+ # req.callback{|response| puts response.content }
+ #
+ def get args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "GET"
+ request args
+ end
+
+ # Post to a url
+ #
+ # req = conn.post('/data')
+ # req.callback{|response| puts response.content }
+ #--
+ # XXX there's no way to supply a POST body.. wtf?
+ def post args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "POST"
+ request args
+ end
+
+
+ #--
+ # Compute and remember a string to be used as the host header in HTTP requests
+ # unless the user overrides it with an argument to #request.
+ #
+ # @private
+ def set_default_host_header host, port, ssl
+ if (ssl and port != 443) or (!ssl and port != 80)
+ @host_header = "#{host}:#{port}"
+ else
+ @host_header = host
+ end
+ end
+
+
+ # @private
+ def post_init
+ super
+ @connected = EM::DefaultDeferrable.new
+ end
+
+ # @private
+ def connection_completed
+ super
+ @connected.succeed
+ end
+
+ #--
+ # All pending requests, if any, must fail.
+ # We might come here without ever passing through connection_completed
+ # in case we can't connect to the server. We'll also get here when the
+ # connection closes (either because the server closes it, or we close it
+ # due to detecting an internal error or security violation).
+ # In either case, run down all pending requests, if any, and signal failure
+ # on them.
+ #
+ # Set and remember a flag (@closed) so we can immediately fail any
+ # subsequent requests.
+ #
+ # @private
+ def unbind
+ super
+ @closed = true
+ (@requests || []).each {|r| r.fail}
+ end
+
+ # @private
+ def request args
+ args[:host_header] = @host_header unless args.has_key?(:host_header)
+ args[:authorization] = @authorization unless args.has_key?(:authorization)
+ r = Request.new self, args
+ if @closed
+ r.fail
+ else
+ (@requests ||= []).unshift r
+ @connected.callback {r.send_request}
+ end
+ r
+ end
+
+ # @private
+ def receive_line ln
+ if req = @requests.last
+ req.receive_line ln
+ else
+ p "??????????"
+ p ln
+ end
+ end
+
+ # @private
+ def receive_binary_data text
+ @requests.last.receive_text text
+ end
+
+ #--
+ # Called by a Request object when it completes.
+ #
+ # @private
+ def pop_request
+ @requests.pop
+ end
+ end
+
+
+=begin
+ class HttpClient2x < Connection
+ include LineText2
+
+ # TODO: Make this behave appropriate in case a #connect fails.
+ # Currently, this produces no errors.
+
+ # Make a connection to a remote HTTP server.
+ # Can take either a pair of arguments (which will be interpreted as
+ # a hostname/ip-address and a port), or a hash.
+ # If the arguments are a hash, then supported values include:
+ # :host => a hostname or ip-address;
+ # :port => a port number
+ #--
+ # TODO, support optional encryption arguments like :ssl
+ def self.connect *args
+ if args.length == 2
+ args = {:host=>args[0], :port=>args[1]}
+ else
+ args = args.first
+ end
+
+ h,prt = args[:host],Integer(args[:port])
+ EM.connect( h, prt, self, h, prt )
+ end
+
+
+ #--
+ # Sugars a connection that makes a single request and then
+ # closes the connection. Matches the behavior and the arguments
+ # of the original implementation of class HttpClient.
+ #
+ # Intended primarily for back compatibility, but the idiom
+ # is probably useful so it's not deprecated.
+ # We return a Deferrable, as did the original implementation.
+ #
+ # Because we're improving the way we deal with errors and exceptions
+ # (specifically, HTTP response codes other than 2xx will trigger the
+ # errback rather than the callback), this may break some existing code.
+ #
+ def self.request args
+ c = connect args
+ end
+
+ #--
+ # Requests can be pipelined. When we get a request, add it to the
+ # front of a queue as an array. The last element of the @requests
+ # array is always the oldest request received. Each element of the
+ # @requests array is a two-element array consisting of a hash with
+ # the original caller's arguments, and an initially-empty Ostruct
+ # containing the data we retrieve from the server's response.
+ # Maintain the instance variable @current_response, which is the response
+ # of the oldest pending request. That's just to make other code a little
+ # easier. If the variable doesn't exist when we come here, we're
+ # obviously the first request being made on the connection.
+ #
+ # The reason for keeping this method private (and requiring use of the
+ # convenience methods #get, #post, #head, etc) is to avoid the small
+ # performance penalty of canonicalizing the verb.
+ #
+ def request args
+ d = EventMachine::DefaultDeferrable.new
+
+ if @closed
+ d.fail
+ return d
+ end
+
+ o = OpenStruct.new
+ o.deferrable = d
+ (@requests ||= []).unshift [args, o]
+ @current_response ||= @requests.last.last
+ @connected.callback {
+ az = args[:authorization] and az = "Authorization: #{az}\r\n"
+
+ r = [
+ "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
+ "Host: #{args[:host_header] || @host_header}\r\n",
+ az || "",
+ "\r\n"
+ ]
+ p r
+ send_data r.join
+ }
+ o.deferrable
+ end
+ private :request
+
+ def get args
+ if args.is_a?(String)
+ args = {:uri=>args}
+ end
+ args[:verb] = "GET"
+ request args
+ end
+
+ def initialize host, port
+ super
+ @host_header = "#{host}:#{port}"
+ end
+ def post_init
+ super
+ @connected = EM::DefaultDeferrable.new
+ end
+
+
+ def connection_completed
+ super
+ @connected.succeed
+ end
+
+ #--
+ # Make sure to throw away any leftover incoming data if we've
+ # been closed due to recognizing an error.
+ #
+ # Generate an internal error if we get an unreasonable number of
+ # header lines. It could be malicious.
+ #
+ def receive_line ln
+ p ln
+ return if @closed
+
+ if ln.length > 0
+ (@current_response.headers ||= []).push ln
+ abort_connection if @current_response.headers.length > 100
+ else
+ process_received_headers
+ end
+ end
+
+ #--
+ # We come here when we've seen all the headers for a particular request.
+ # What we do next depends on the response line (which should be the
+ # first line in the header set), and whether there is content to read.
+ # We may transition into a text-reading state to read content, or
+ # we may abort the connection, or we may go right back into parsing
+ # responses for the next response in the chain.
+ #
+ # We make an ASSUMPTION that the first line is an HTTP response.
+ # Anything else produces an error that aborts the connection.
+ # This may not be enough, because it may be that responses to pipelined
+ # requests will come with a blank-line delimiter.
+ #
+ # Any non-2xx response will be treated as a fatal error, and abort the
+ # connection. We will set up the status and other response parameters.
+ # TODO: we will want to properly support 1xx responses, which some versions
+ # of IIS copiously generate.
+ # TODO: We need to give the option of not aborting the connection with certain
+ # non-200 responses, in order to work with NTLM and other authentication
+ # schemes that work at the level of individual connections.
+ #
+ # Some error responses will get sugarings. For example, we'll return the
+ # Location header in the response in case of a 301/302 response.
+ #
+ # Possible dispositions here:
+ # 1) No content to read (either content-length is zero or it's a HEAD request);
+ # 2) Switch to text mode to read a specific number of bytes;
+ # 3) Read a chunked or multipart response;
+ # 4) Read till the server closes the connection.
+ #
+ # Our reponse to the client can be either to wait till all the content
+ # has been read and then to signal caller's deferrable, or else to signal
+ # it when we finish the processing the headers and then expect the caller
+ # to have given us a block to call as the content comes in. And of course
+ # the latter gets stickier with chunks and multiparts.
+ #
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
+ ClenRE = /\AContent-length:\s*(\d+)/i
+ def process_received_headers
+ abort_connection unless @current_response.headers.first =~ HttpResponseRE
+ @current_response.version = $1.dup
+ st = $2.dup
+ @current_response.status = st.to_i
+ abort_connection unless st[0,1] == "2"
+
+ clen = nil
+ @current_response.headers.each do |e|
+ if clen == nil and e =~ ClenRE
+ clen = $1.dup.to_i
+ end
+ end
+
+ if clen
+ set_text_mode clen
+ end
+ end
+ private :process_received_headers
+
+
+ def receive_binary_data text
+ @current_response.content = text
+ @current_response.deferrable.succeed @current_response
+ @requests.pop
+ @current_response = (@requests.last || []).last
+ set_line_mode
+ end
+
+
+
+ # We've received either a server error or an internal error.
+ # Close the connection and abort any pending requests.
+ #--
+ # When should we call close_connection? It will cause #unbind
+ # to be fired. Should the user expect to see #unbind before
+ # we call #receive_http_error, or the other way around?
+ #
+ # Set instance variable @closed. That's used to inhibit further
+ # processing of any inbound data after an error has been recognized.
+ #
+ # We shouldn't have to worry about any leftover outbound data,
+ # because we call close_connection (not close_connection_after_writing).
+ # That ensures that any pipelined requests received after an error
+ # DO NOT get streamed out to the server on this connection.
+ # Very important. TODO, write a unit-test to establish that behavior.
+ #
+ def abort_connection
+ close_connection
+ @closed = true
+ @current_response.deferrable.fail( @current_response )
+ end
+
+
+ #------------------------
+ # Below here are user-overridable methods.
+
+ end
+=end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb
new file mode 100644
index 0000000..784daf2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_and_text.rb
@@ -0,0 +1,125 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+ # A protocol that handles line-oriented data with interspersed binary text.
+ #
+ # This version is optimized for performance. See EventMachine::Protocols::LineText2
+ # for a version which is optimized for correctness with regard to binary text blocks
+ # that can switch back to line mode.
+ class LineAndTextProtocol < Connection
+ MaxBinaryLength = 32*1024*1024
+
+ def initialize *args
+ super
+ lbp_init_line_state
+ end
+
+ def receive_data data
+ if @lbp_mode == :lines
+ begin
+ @lpb_buffer.extract(data).each do |line|
+ receive_line(line.chomp) if respond_to?(:receive_line)
+ end
+ rescue
+ receive_error('overlength line') if respond_to?(:receive_error)
+ close_connection
+ return
+ end
+ else
+ if @lbp_binary_limit > 0
+ wanted = @lbp_binary_limit - @lbp_binary_bytes_received
+ chunk = nil
+ if data.length > wanted
+ chunk = data.slice!(0...wanted)
+ else
+ chunk = data
+ data = ""
+ end
+ @lbp_binary_buffer[@lbp_binary_bytes_received...(@lbp_binary_bytes_received+chunk.length)] = chunk
+ @lbp_binary_bytes_received += chunk.length
+ if @lbp_binary_bytes_received == @lbp_binary_limit
+ receive_binary_data(@lbp_binary_buffer) if respond_to?(:receive_binary_data)
+ lbp_init_line_state
+ end
+ receive_data(data) if data.length > 0
+ else
+ receive_binary_data(data) if respond_to?(:receive_binary_data)
+ data = ""
+ end
+ end
+ end
+
+ def unbind
+ if @lbp_mode == :binary and @lbp_binary_limit > 0
+ if respond_to?(:receive_binary_data)
+ receive_binary_data( @lbp_binary_buffer[0...@lbp_binary_bytes_received] )
+ end
+ end
+ end
+
+ # Set up to read the supplied number of binary bytes.
+ # This recycles all the data currently waiting in the line buffer, if any.
+ # If the limit is nil, then ALL subsequent data will be treated as binary
+ # data and passed to the upstream protocol handler as we receive it.
+ # If a limit is given, we'll hold the incoming binary data and not
+ # pass it upstream until we've seen it all, or until there is an unbind
+ # (in which case we'll pass up a partial).
+ # Specifying nil for the limit (the default) means there is no limit.
+ # Specifiyng zero for the limit will cause an immediate transition back to line mode.
+ #
+ def set_binary_mode size = nil
+ if @lbp_mode == :lines
+ if size == 0
+ receive_binary_data("") if respond_to?(:receive_binary_data)
+ # Do no more work here. Stay in line mode and keep consuming data.
+ else
+ @lbp_binary_limit = size.to_i # (nil will be stored as zero)
+ if @lbp_binary_limit > 0
+ raise "Overlength" if @lbp_binary_limit > MaxBinaryLength # arbitrary sanity check
+ @lbp_binary_buffer = "\0" * @lbp_binary_limit
+ @lbp_binary_bytes_received = 0
+ end
+
+ @lbp_mode = :binary
+ receive_data @lpb_buffer.flush
+ end
+ else
+ raise "invalid operation"
+ end
+ end
+
+ #--
+ # For internal use, establish protocol baseline for handling lines.
+ def lbp_init_line_state
+ @lpb_buffer = BufferedTokenizer.new("\n")
+ @lbp_mode = :lines
+ end
+ private :lbp_init_line_state
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb
new file mode 100644
index 0000000..dfddae8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/line_protocol.rb
@@ -0,0 +1,29 @@
+module EventMachine
+ module Protocols
+ # LineProtocol will parse out newline terminated strings from a receive_data stream
+ #
+ # module Server
+ # include EM::P::LineProtocol
+ #
+ # def receive_line(line)
+ # send_data("you said: #{line}")
+ # end
+ # end
+ #
+ module LineProtocol
+ # @private
+ def receive_data data
+ (@buf ||= '') << data
+
+ while @buf.slice!(/(.*?)\r?\n/)
+ receive_line($1)
+ end
+ end
+
+ # Invoked with lines received over the network
+ def receive_line(line)
+ # stub
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb
new file mode 100644
index 0000000..9fdf28b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/linetext2.rb
@@ -0,0 +1,179 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+ # In the grand, time-honored tradition of re-inventing the wheel, we offer
+ # here YET ANOTHER protocol that handles line-oriented data with interspersed
+ # binary text. This one trades away some of the performance optimizations of
+ # EventMachine::Protocols::LineAndTextProtocol in order to get better correctness
+ # with regard to binary text blocks that can switch back to line mode. It also
+ # permits the line-delimiter to change in midstream.
+ # This was originally written to support Stomp.
+ module LineText2
+ # TODO! We're not enforcing the limits on header lengths and text-lengths.
+ # When we get around to that, call #receive_error if the user defined it, otherwise
+ # throw exceptions.
+
+ MaxBinaryLength = 32*1024*1024
+
+ #--
+ # Will loop internally until there's no data left to read.
+ # That way the user-defined handlers we call can modify the
+ # handling characteristics on a per-token basis.
+ #
+ def receive_data data
+ return unless (data and data.length > 0)
+
+ # Do this stuff in lieu of a constructor.
+ @lt2_mode ||= :lines
+ @lt2_delimiter ||= "\n"
+ @lt2_linebuffer ||= []
+
+ remaining_data = data
+
+ while remaining_data.length > 0
+ if @lt2_mode == :lines
+ delimiter_string = case @lt2_delimiter
+ when Regexp
+ remaining_data.slice(@lt2_delimiter)
+ else
+ @lt2_delimiter
+ end
+ ix = remaining_data.index(delimiter_string) if delimiter_string
+ if ix
+ @lt2_linebuffer << remaining_data[0...ix]
+ ln = @lt2_linebuffer.join
+ @lt2_linebuffer.clear
+ if @lt2_delimiter == "\n"
+ ln.chomp!
+ end
+ receive_line ln
+ remaining_data = remaining_data[(ix+delimiter_string.length)..-1]
+ else
+ @lt2_linebuffer << remaining_data
+ remaining_data = ""
+ end
+ elsif @lt2_mode == :text
+ if @lt2_textsize
+ needed = @lt2_textsize - @lt2_textpos
+ will_take = if remaining_data.length > needed
+ needed
+ else
+ remaining_data.length
+ end
+
+ @lt2_textbuffer << remaining_data[0...will_take]
+ tail = remaining_data[will_take..-1]
+
+ @lt2_textpos += will_take
+ if @lt2_textpos >= @lt2_textsize
+ # Reset line mode (the default behavior) BEFORE calling the
+ # receive_binary_data. This makes it possible for user code
+ # to call set_text_mode, enabling chains of text blocks
+ # (which can possibly be of different sizes).
+ set_line_mode
+ receive_binary_data @lt2_textbuffer.join
+ receive_end_of_binary_data
+ end
+
+ remaining_data = tail
+ else
+ receive_binary_data remaining_data
+ remaining_data = ""
+ end
+ end
+ end
+ end
+
+ # The line delimiter may be a regular expression or a string. Anything
+ # passed to set_delimiter other than a regular expression will be
+ # converted to a string.
+ def set_delimiter delim
+ @lt2_delimiter = case delim
+ when Regexp
+ delim
+ else
+ delim.to_s
+ end
+ end
+
+ # Called internally but also exposed to user code, for the case in which
+ # processing of binary data creates a need to transition back to line mode.
+ # We support an optional parameter to "throw back" some data, which might
+ # be an umprocessed chunk of the transmitted binary data, or something else
+ # entirely.
+ def set_line_mode data=""
+ @lt2_mode = :lines
+ (@lt2_linebuffer ||= []).clear
+ receive_data data.to_s
+ end
+
+ def set_text_mode size=nil
+ if size == 0
+ set_line_mode
+ else
+ @lt2_mode = :text
+ (@lt2_textbuffer ||= []).clear
+ @lt2_textsize = size # which can be nil, signifying no limit
+ @lt2_textpos = 0
+ end
+ end
+
+ # Alias for #set_text_mode, added for back-compatibility with LineAndTextProtocol.
+ def set_binary_mode size=nil
+ set_text_mode size
+ end
+
+ # In case of a dropped connection, we'll send a partial buffer to user code
+ # when in sized text mode. User overrides of #receive_binary_data need to
+ # be aware that they may get a short buffer.
+ def unbind
+ @lt2_mode ||= nil
+ if @lt2_mode == :text and @lt2_textpos > 0
+ receive_binary_data @lt2_textbuffer.join
+ end
+ end
+
+ # Stub. Should be subclassed by user code.
+ def receive_line ln
+ # no-op
+ end
+
+ # Stub. Should be subclassed by user code.
+ def receive_binary_data data
+ # no-op
+ end
+
+ # Stub. Should be subclassed by user code.
+ # This is called when transitioning internally from text mode
+ # back to line mode. Useful when client code doesn't want
+ # to keep track of how much data it's received.
+ def receive_end_of_binary_data
+ # no-op
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb
new file mode 100644
index 0000000..1f81aaf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/memcache.rb
@@ -0,0 +1,331 @@
+module EventMachine
+ module Protocols
+ # Implements the Memcache protocol (http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt).
+ # Requires memcached >= 1.2.4 w/ noreply support
+ #
+ # == Usage example
+ #
+ # EM.run{
+ # cache = EM::P::Memcache.connect 'localhost', 11211
+ #
+ # cache.set :a, 'hello'
+ # cache.set :b, 'hi'
+ # cache.set :c, 'how are you?'
+ # cache.set :d, ''
+ #
+ # cache.get(:a){ |v| p v }
+ # cache.get_hash(:a, :b, :c, :d){ |v| p v }
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
+ #
+ # cache.get(:a,:z,:b,:y,:d){ |a,z,b,y,d| p [a,z,b,y,d] }
+ #
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # cache.set(:missing, 'abc'){ p :stored }
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # cache.del(:missing){ p :deleted }
+ # cache.get(:missing){ |m| p [:missing=, m] }
+ # }
+ #
+ module Memcache
+ include EM::Deferrable
+
+ ##
+ # constants
+
+ unless defined? Cempty
+ # @private
+ Cstored = 'STORED'.freeze
+ # @private
+ Cend = 'END'.freeze
+ # @private
+ Cdeleted = 'DELETED'.freeze
+ # @private
+ Cunknown = 'NOT_FOUND'.freeze
+ # @private
+ Cerror = 'ERROR'.freeze
+
+ # @private
+ Cempty = ''.freeze
+ # @private
+ Cdelimiter = "\r\n".freeze
+ end
+
+ ##
+ # commands
+
+ # Get the value associated with one or multiple keys
+ #
+ # cache.get(:a){ |v| p v }
+ # cache.get(:a,:b,:c,:d){ |a,b,c,d| p [a,b,c,d] }
+ #
+ def get *keys
+ raise ArgumentError unless block_given?
+
+ callback{
+ keys = keys.map{|k| k.to_s.gsub(/\s/,'_') }
+ send_data "get #{keys.join(' ')}\r\n"
+ @get_cbs << [keys, proc{ |values|
+ yield *keys.map{ |k| values[k] }
+ }]
+ }
+ end
+
+ # Set the value for a given key
+ #
+ # cache.set :a, 'hello'
+ # cache.set(:missing, 'abc'){ puts "stored the value!" }
+ #
+ def set key, val, exptime = 0, &cb
+ callback{
+ val = val.to_s
+ send_cmd :set, key, 0, exptime, val.respond_to?(:bytesize) ? val.bytesize : val.size, !block_given?
+ send_data val
+ send_data Cdelimiter
+ @set_cbs << cb if cb
+ }
+ end
+
+ # Gets multiple values as a hash
+ #
+ # cache.get_hash(:a, :b, :c, :d){ |h| puts h[:a] }
+ #
+ def get_hash *keys
+ raise ArgumentError unless block_given?
+
+ get *keys do |*values|
+ yield keys.inject({}){ |hash, k| hash.update k => values[keys.index(k)] }
+ end
+ end
+
+ # Delete the value associated with a key
+ #
+ # cache.del :a
+ # cache.del(:b){ puts "deleted the value!" }
+ #
+ def delete key, expires = 0, &cb
+ callback{
+ send_data "delete #{key} #{expires}#{cb ? '' : ' noreply'}\r\n"
+ @del_cbs << cb if cb
+ }
+ end
+ alias del delete
+
+ # Connect to a memcached server (must support NOREPLY, memcached >= 1.2.4)
+ def self.connect host = 'localhost', port = 11211
+ EM.connect host, port, self, host, port
+ end
+
+ def send_cmd cmd, key, flags = 0, exptime = 0, bytes = 0, noreply = false
+ send_data "#{cmd} #{key} #{flags} #{exptime} #{bytes}#{noreply ? ' noreply' : ''}\r\n"
+ end
+ private :send_cmd
+
+ ##
+ # errors
+
+ # @private
+ class ParserError < StandardError
+ end
+
+ ##
+ # em hooks
+
+ # @private
+ def initialize host, port = 11211
+ @host, @port = host, port
+ end
+
+ # @private
+ def connection_completed
+ @get_cbs = []
+ @set_cbs = []
+ @del_cbs = []
+
+ @values = {}
+
+ @reconnecting = false
+ @connected = true
+ succeed
+ # set_delimiter "\r\n"
+ # set_line_mode
+ end
+
+ #--
+ # 19Feb09 Switched to a custom parser, LineText2 is recursive and can cause
+ # stack overflows when there is too much data.
+ # include EM::P::LineText2
+ # @private
+ def receive_data data
+ (@buffer||='') << data
+
+ while index = @buffer.index(Cdelimiter)
+ begin
+ line = @buffer.slice!(0,index+2)
+ process_cmd line
+ rescue ParserError
+ @buffer[0...0] = line
+ break
+ end
+ end
+ end
+
+ #--
+ # def receive_line line
+ # @private
+ def process_cmd line
+ case line.strip
+ when /^VALUE\s+(.+?)\s+(\d+)\s+(\d+)/ # VALUE <key> <flags> <bytes>
+ bytes = Integer($3)
+ # set_binary_mode bytes+2
+ # @cur_key = $1
+ if @buffer.size >= bytes + 2
+ @values[$1] = @buffer.slice!(0,bytes)
+ @buffer.slice!(0,2) # \r\n
+ else
+ raise ParserError
+ end
+
+ when Cend # END
+ if entry = @get_cbs.shift
+ keys, cb = entry
+ cb.call(@values)
+ end
+ @values = {}
+
+ when Cstored # STORED
+ if cb = @set_cbs.shift
+ cb.call(true)
+ end
+
+ when Cdeleted # DELETED
+ if cb = @del_cbs.shift
+ cb.call(true)
+ end
+
+ when Cunknown # NOT_FOUND
+ if cb = @del_cbs.shift
+ cb.call(false)
+ end
+
+ else
+ p [:MEMCACHE_UNKNOWN, line]
+ end
+ end
+
+ #--
+ # def receive_binary_data data
+ # @values[@cur_key] = data[0..-3]
+ # end
+
+ # @private
+ def unbind
+ if @connected or @reconnecting
+ EM.add_timer(1){ reconnect @host, @port }
+ @connected = false
+ @reconnecting = true
+ @deferred_status = nil
+ else
+ raise 'Unable to connect to memcached server'
+ end
+ end
+ end
+ end
+end
+
+if __FILE__ == $0
+ # ruby -I ext:lib -r eventmachine -rubygems lib/protocols/memcache.rb
+ require 'em/spec'
+
+ # @private
+ class TestConnection
+ include EM::P::Memcache
+ def send_data data
+ sent_data << data
+ end
+ def sent_data
+ @sent_data ||= ''
+ end
+
+ def initialize
+ connection_completed
+ end
+ end
+
+ EM.describe EM::Protocols::Memcache do
+
+ before{
+ @c = TestConnection.new
+ }
+
+ should 'send get requests' do
+ @c.get('a'){}
+ @c.sent_data.should == "get a\r\n"
+ done
+ end
+
+ should 'send set requests' do
+ @c.set('a', 1){}
+ @c.sent_data.should == "set a 0 0 1\r\n1\r\n"
+ done
+ end
+
+ should 'use noreply on set without block' do
+ @c.set('a', 1)
+ @c.sent_data.should == "set a 0 0 1 noreply\r\n1\r\n"
+ done
+ end
+
+ should 'send delete requests' do
+ @c.del('a')
+ @c.sent_data.should == "delete a 0 noreply\r\n"
+ done
+ end
+
+ should 'work when get returns no values' do
+ @c.get('a'){ |a|
+ a.should.be.nil
+ done
+ }
+
+ @c.receive_data "END\r\n"
+ end
+
+ should 'invoke block on set' do
+ @c.set('a', 1){
+ done
+ }
+
+ @c.receive_data "STORED\r\n"
+ end
+
+ should 'invoke block on delete' do
+ @c.delete('a'){ |found|
+ found.should.be.false
+ }
+ @c.delete('b'){ |found|
+ found.should.be.true
+ done
+ }
+
+ @c.receive_data "NOT_FOUND\r\n"
+ @c.receive_data "DELETED\r\n"
+ end
+
+ should 'parse split responses' do
+ @c.get('a'){ |a|
+ a.should == 'abc'
+ done
+ }
+
+ @c.receive_data "VAL"
+ @c.receive_data "UE a 0 "
+ @c.receive_data "3\r\n"
+ @c.receive_data "ab"
+ @c.receive_data "c"
+ @c.receive_data "\r\n"
+ @c.receive_data "EN"
+ @c.receive_data "D\r\n"
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb
new file mode 100644
index 0000000..ec79cb4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/object_protocol.rb
@@ -0,0 +1,46 @@
+module EventMachine
+ module Protocols
+ # ObjectProtocol allows for easy communication using marshaled ruby objects
+ #
+ # module RubyServer
+ # include EM::P::ObjectProtocol
+ #
+ # def receive_object obj
+ # send_object({'you said' => obj})
+ # end
+ # end
+ #
+ module ObjectProtocol
+ # By default returns Marshal, override to return JSON or YAML, or any
+ # other serializer/deserializer responding to #dump and #load.
+ def serializer
+ Marshal
+ end
+
+ # @private
+ def receive_data data
+ (@buf ||= '') << data
+
+ while @buf.size >= 4
+ if @buf.size >= 4+(size=@buf.unpack('N').first)
+ @buf.slice!(0,4)
+ receive_object serializer.load(@buf.slice!(0,size))
+ else
+ break
+ end
+ end
+ end
+
+ # Invoked with ruby objects received over the network
+ def receive_object obj
+ # stub
+ end
+
+ # Sends a ruby object over the network
+ def send_object obj
+ data = serializer.dump(obj)
+ send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*')
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb
new file mode 100644
index 0000000..7d87505
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/postgres3.rb
@@ -0,0 +1,246 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+require 'postgres-pr/message'
+require 'postgres-pr/connection'
+require 'stringio'
+
+# @private
+class StringIO
+ # Reads exactly +n+ bytes.
+ #
+ # If the data read is nil an EOFError is raised.
+ #
+ # If the data read is too short an IOError is raised
+ def readbytes(n)
+ str = read(n)
+ if str == nil
+ raise EOFError, "End of file reached"
+ end
+ if str.size < n
+ raise IOError, "data truncated"
+ end
+ str
+ end
+ alias read_exactly_n_bytes readbytes
+end
+
+
+module EventMachine
+ module Protocols
+ # PROVISIONAL IMPLEMENTATION of an evented Postgres client.
+ # This implements version 3 of the Postgres wire protocol, which will work
+ # with any Postgres version from roughly 7.4 onward.
+ #
+ # Objective: we want to access Postgres databases without requiring threads.
+ # Until now this has been a problem because the Postgres client implementations
+ # have all made use of blocking I/O calls, which is incompatible with a
+ # thread-free evented model.
+ #
+ # But rather than re-implement the Postgres Wire3 protocol, we're taking advantage
+ # of the existing postgres-pr library, which was originally written by Michael
+ # Neumann but (at this writing) appears to be no longer maintained. Still, it's
+ # in basically a production-ready state, and the wire protocol isn't that complicated
+ # anyway.
+ #
+ # We're tucking in a bunch of require statements that may not be present in garden-variety
+ # EM installations. Until we find a good way to only require these if a program
+ # requires postgres, this file will need to be required explicitly.
+ #
+ # We need to monkeypatch StringIO because it lacks the #readbytes method needed
+ # by postgres-pr.
+ # The StringIO monkeypatch is lifted from the standard library readbytes.rb,
+ # which adds method #readbytes directly to class IO. But StringIO is not a subclass of IO.
+ # It is modified to raise an IOError instead of TruncatedDataException since the exception is unused.
+ #
+ # We cloned the handling of postgres messages from lib/postgres-pr/connection.rb
+ # in the postgres-pr library, and modified it for event-handling.
+ #
+ # TODO: The password handling in dispatch_conn_message is totally incomplete.
+ #
+ #
+ # We return Deferrables from the user-level operations surfaced by this interface.
+ # Experimentally, we're using the pattern of always returning a boolean value as the
+ # first argument of a deferrable callback to indicate success or failure. This is
+ # instead of the traditional pattern of calling Deferrable#succeed or #fail, and
+ # requiring the user to define both a callback and an errback function.
+ #
+ # === Usage
+ # EM.run {
+ # db = EM.connect_unix_domain( "/tmp/.s.PGSQL.5432", EM::P::Postgres3 )
+ # db.connect( dbname, username, psw ).callback do |status|
+ # if status
+ # db.query( "select * from some_table" ).callback do |status, result, errors|
+ # if status
+ # result.rows.each do |row|
+ # p row
+ # end
+ # end
+ # end
+ # end
+ # end
+ # }
+ class Postgres3 < EventMachine::Connection
+ include PostgresPR
+
+ def initialize
+ @data = ""
+ @params = {}
+ end
+
+ def connect db, user, psw=nil
+ d = EM::DefaultDeferrable.new
+ d.timeout 15
+
+ if @pending_query || @pending_conn
+ d.succeed false, "Operation already in progress"
+ else
+ @pending_conn = d
+ prms = {"user"=>user, "database"=>db}
+ @user = user
+ if psw
+ @password = psw
+ #prms["password"] = psw
+ end
+ send_data PostgresPR::StartupMessage.new( 3 << 16, prms ).dump
+ end
+
+ d
+ end
+
+ def query sql
+ d = EM::DefaultDeferrable.new
+ d.timeout 15
+
+ if @pending_query || @pending_conn
+ d.succeed false, "Operation already in progress"
+ else
+ @r = PostgresPR::Connection::Result.new
+ @e = []
+ @pending_query = d
+ send_data PostgresPR::Query.dump(sql)
+ end
+
+ d
+ end
+
+
+ def receive_data data
+ @data << data
+ while @data.length >= 5
+ pktlen = @data[1...5].unpack("N").first
+ if @data.length >= (1 + pktlen)
+ pkt = @data.slice!(0...(1+pktlen))
+ m = StringIO.open( pkt, "r" ) {|io| PostgresPR::Message.read( io ) }
+ if @pending_conn
+ dispatch_conn_message m
+ elsif @pending_query
+ dispatch_query_message m
+ else
+ raise "Unexpected message from database"
+ end
+ else
+ break # very important, break out of the while
+ end
+ end
+ end
+
+
+ def unbind
+ if o = (@pending_query || @pending_conn)
+ o.succeed false, "lost connection"
+ end
+ end
+
+ # Cloned and modified from the postgres-pr.
+ def dispatch_conn_message msg
+ case msg
+ when AuthentificationClearTextPassword
+ raise ArgumentError, "no password specified" if @password.nil?
+ send_data PasswordMessage.new(@password).dump
+
+ when AuthentificationCryptPassword
+ raise ArgumentError, "no password specified" if @password.nil?
+ send_data PasswordMessage.new(@password.crypt(msg.salt)).dump
+
+ when AuthentificationMD5Password
+ raise ArgumentError, "no password specified" if @password.nil?
+ require 'digest/md5'
+
+ m = Digest::MD5.hexdigest(@password + @user)
+ m = Digest::MD5.hexdigest(m + msg.salt)
+ m = 'md5' + m
+ send_data PasswordMessage.new(m).dump
+
+ when AuthentificationKerberosV4, AuthentificationKerberosV5, AuthentificationSCMCredential
+ raise "unsupported authentification"
+
+ when AuthentificationOk
+ when ErrorResponse
+ raise msg.field_values.join("\t")
+ when NoticeResponse
+ @notice_processor.call(msg) if @notice_processor
+ when ParameterStatus
+ @params[msg.key] = msg.value
+ when BackendKeyData
+ # TODO
+ #p msg
+ when ReadyForQuery
+ # TODO: use transaction status
+ pc,@pending_conn = @pending_conn,nil
+ pc.succeed true
+ else
+ raise "unhandled message type"
+ end
+ end
+
+ # Cloned and modified from the postgres-pr.
+ def dispatch_query_message msg
+ case msg
+ when DataRow
+ @r.rows << msg.columns
+ when CommandComplete
+ @r.cmd_tag = msg.cmd_tag
+ when ReadyForQuery
+ pq,@pending_query = @pending_query,nil
+ pq.succeed true, @r, @e
+ when RowDescription
+ @r.fields = msg.fields
+ when CopyInResponse
+ when CopyOutResponse
+ when EmptyQueryResponse
+ when ErrorResponse
+ # TODO
+ @e << msg
+ when NoticeResponse
+ @notice_processor.call(msg) if @notice_processor
+ else
+ # TODO
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb
new file mode 100644
index 0000000..9cabc51
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/saslauth.rb
@@ -0,0 +1,175 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # Implements SASL authd.
+ # This is a very, very simple protocol that mimics the one used
+ # by saslauthd and pwcheck, two outboard daemons included in the
+ # standard SASL library distro.
+ # The only thing this is really suitable for is SASL PLAIN
+ # (user+password) authentication, but the SASL libs that are
+ # linked into standard servers (like imapd and sendmail) implement
+ # the other ones.
+ #
+ # SASL-auth is intended for reasonably fast operation inside a
+ # single machine, so it has no transport-security (although there
+ # have been multi-machine extensions incorporating transport-layer
+ # encryption).
+ #
+ # The standard saslauthd module generally runs privileged and does
+ # its work by referring to the system-account files.
+ #
+ # This feature was added to EventMachine to enable the development
+ # of custom authentication/authorization engines for standard servers.
+ #
+ # To use SASLauth, include it in a class that subclasses EM::Connection,
+ # and reimplement the validate method.
+ #
+ # The typical way to incorporate this module into an authentication
+ # daemon would be to set it as the handler for a UNIX-domain socket.
+ # The code might look like this:
+ #
+ # EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )
+ # File.chmod( 0777, "/var/run/saslauthd/mux")
+ #
+ # The chmod is probably needed to ensure that unprivileged clients can
+ # access the UNIX-domain socket.
+ #
+ # It's also a very good idea to drop superuser privileges (if any), after
+ # the UNIX-domain socket has been opened.
+ #--
+ # Implementation details: assume the client can send us pipelined requests,
+ # and that the client will close the connection.
+ #
+ # The client sends us four values, each encoded as a two-byte length field in
+ # network order followed by the specified number of octets.
+ # The fields specify the username, password, service name (such as imap),
+ # and the "realm" name. We send back the barest minimum reply, a single
+ # field also encoded as a two-octet length in network order, followed by
+ # either "NO" or "OK" - simplicity itself.
+ #
+ # We enforce a maximum field size just as a sanity check.
+ # We do NOT automatically time out the connection.
+ #
+ # The code we use to parse out the values is ugly and probably slow.
+ # Improvements welcome.
+ #
+ module SASLauth
+
+ MaxFieldSize = 128*1024
+ def post_init
+ super
+ @sasl_data = ""
+ @sasl_values = []
+ end
+
+ def receive_data data
+ @sasl_data << data
+ while @sasl_data.length >= 2
+ len = (@sasl_data[0,2].unpack("n")).first
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
+ if @sasl_data.length >= (len + 2)
+ @sasl_values << @sasl_data[2,len]
+ @sasl_data.slice!(0...(2+len))
+ if @sasl_values.length == 4
+ send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )
+ @sasl_values.clear
+ end
+ else
+ break
+ end
+ end
+ end
+
+ def validate username, psw, sysname, realm
+ p username
+ p psw
+ p sysname
+ p realm
+ true
+ end
+ end
+
+ # Implements the SASL authd client protocol.
+ # This is a very, very simple protocol that mimics the one used
+ # by saslauthd and pwcheck, two outboard daemons included in the
+ # standard SASL library distro.
+ # The only thing this is really suitable for is SASL PLAIN
+ # (user+password) authentication, but the SASL libs that are
+ # linked into standard servers (like imapd and sendmail) implement
+ # the other ones.
+ #
+ # You can use this module directly as a handler for EM Connections,
+ # or include it in a module or handler class of your own.
+ #
+ # First connect to a SASL server (it's probably a TCP server, or more
+ # likely a Unix-domain socket). Then call the #validate? method,
+ # passing at least a username and a password. #validate? returns
+ # a Deferrable which will either succeed or fail, depending
+ # on the status of the authentication operation.
+ #
+ module SASLauthclient
+ MaxFieldSize = 128*1024
+
+ def validate? username, psw, sysname=nil, realm=nil
+
+ str = [username, psw, sysname, realm].map {|m|
+ [(m || "").length, (m || "")]
+ }.flatten.pack( "nA*" * 4 )
+ send_data str
+
+ d = EM::DefaultDeferrable.new
+ @queries.unshift d
+ d
+ end
+
+ def post_init
+ @sasl_data = ""
+ @queries = []
+ end
+
+ def receive_data data
+ @sasl_data << data
+
+ while @sasl_data.length > 2
+ len = (@sasl_data[0,2].unpack("n")).first
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
+ if @sasl_data.length >= (len + 2)
+ val = @sasl_data[2,len]
+ @sasl_data.slice!(0...(2+len))
+ q = @queries.pop
+ (val == "NO") ? q.fail : q.succeed
+ else
+ break
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb
new file mode 100644
index 0000000..25ad17a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpclient.rb
@@ -0,0 +1,394 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+require 'ostruct'
+
+module EventMachine
+ module Protocols
+
+ # Simple SMTP client
+ #
+ # @example
+ # email = EM::Protocols::SmtpClient.send(
+ # :domain=>"example.com",
+ # :host=>'localhost',
+ # :port=>25, # optional, defaults 25
+ # :starttls=>true, # use ssl
+ # :from=>"sender@example.com",
+ # :to=> ["to_1@example.com", "to_2@example.com"],
+ # :header=> {"Subject" => "This is a subject line"},
+ # :body=> "This is the body of the email"
+ # )
+ # email.callback{
+ # puts 'Email sent!'
+ # }
+ # email.errback{ |e|
+ # puts 'Email failed!'
+ # }
+ #
+ # Sending generated emails (using Mail)
+ #
+ # mail = Mail.new do
+ # from 'alice@example.com'
+ # to 'bob@example.com'
+ # subject 'This is a test email'
+ # body 'Hello, world!'
+ # end
+ #
+ # email = EM::P::SmtpClient.send(
+ # :domain=>'example.com',
+ # :from=>mail.from.first,
+ # :to=>mail.to,
+ # :message=>mail.to_s
+ # )
+ #
+ class SmtpClient < Connection
+ include EventMachine::Deferrable
+ include EventMachine::Protocols::LineText2
+
+ def initialize
+ @succeeded = nil
+ @responder = nil
+ @code = nil
+ @msg = nil
+ end
+
+ # :host => required String
+ # a string containing the IP address or host name of the SMTP server to connect to.
+ # :port => optional
+ # defaults to 25.
+ # :domain => required String
+ # This is passed as the argument to the EHLO command.
+ # :starttls => optional Boolean
+ # If it evaluates true, then the client will initiate STARTTLS with
+ # the server, and abort the connection if the negotiation doesn't succeed.
+ # TODO, need to be able to pass certificate parameters with this option.
+ # :auth => optional Hash of auth parameters
+ # If not given, then no auth will be attempted.
+ # (In that case, the connection will be aborted if the server requires auth.)
+ # Specify the hash value :type to determine the auth type, along with additional parameters
+ # depending on the type.
+ # Currently only :type => :plain is supported. Pass additional parameters :username (String),
+ # and :password (either a String or a Proc that will be called at auth-time).
+ #
+ # @example
+ # :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
+ #
+ # :from => required String
+ # Specifies the sender of the message. Will be passed as the argument
+ # to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
+ # The connection will abort if the server rejects the value.
+ # :to => required String or Array of Strings
+ # The recipient(s) of the message. Do NOT enclose
+ # any of the values in angle-brackets (<>) characters. It's NOT a fatal error if one or more
+ # recipients are rejected by the server. (Of course, if ALL of them are, the server will most
+ # likely trigger an error when we try to send data.) An array of codes containing the status
+ # of each requested recipient is available after the call completes. TODO, we should define
+ # an overridable stub that will be called on rejection of a recipient or a sender, giving
+ # user code the chance to try again or abort the connection.
+ #
+ # One of either :message, :content, or :header and :body is required:
+ #
+ # :message => String
+ # A valid RFC2822 Internet Message.
+ # :content => String
+ # Raw data which MUST be in correct SMTP body format, with escaped leading dots and a trailing
+ # dot line.
+ # :header => String or Hash of values to be transmitted in the header of the message.
+ # The hash keys are the names of the headers (do NOT append a trailing colon), and the values
+ # are strings containing the header values. TODO, support Arrays of header values, which would
+ # cause us to send that specific header line more than once.
+ #
+ # @example
+ # :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
+ #
+ # :body => Optional String or Array of Strings, defaults blank.
+ # This will be passed as the body of the email message.
+ # TODO, this needs to be significantly beefed up. As currently written, this requires the caller
+ # to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
+ # SMTP transmission format. We need to be able to automatically convert binary data, and add
+ # correct line-breaks to text data.
+ #
+ # :verbose => Optional.
+ # If true, will cause a lot of information (including the server-side of the
+ # conversation) to be dumped to $>.
+ #
+ def self.send args={}
+ args[:port] ||= 25
+ args[:body] ||= ""
+
+=begin
+ (I don't think it's possible for EM#connect to throw an exception under normal
+ circumstances, so this original code is stubbed out. A connect-failure will result
+ in the #unbind method being called without calling #connection_completed.)
+ begin
+ EventMachine.connect( args[:host], args[:port], self) {|c|
+ # According to the EM docs, we will get here AFTER post_init is called.
+ c.args = args
+ c.set_comm_inactivity_timeout 60
+ }
+ rescue
+ # We'll get here on a connect error. This code mimics the effect
+ # of a call to invoke_internal_error. Would be great to DRY this up.
+ # (Actually, it may be that we never get here, if EM#connect catches
+ # its errors internally.)
+ d = EM::DefaultDeferrable.new
+ d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
+ d
+ end
+=end
+ EventMachine.connect( args[:host], args[:port], self) {|c|
+ # According to the EM docs, we will get here AFTER post_init is called.
+ c.args = args
+ c.set_comm_inactivity_timeout 60
+ }
+ end
+
+ attr_writer :args
+
+ # @private
+ def post_init
+ @return_values = OpenStruct.new
+ @return_values.start_time = Time.now
+ end
+
+ # @private
+ def connection_completed
+ @responder = :receive_signon
+ @msg = []
+ end
+
+ # We can get here in a variety of ways, all of them being failures unless
+ # the @succeeded flag is set. If a protocol success was recorded, then don't
+ # set a deferred success because the caller will already have done it
+ # (no need to wait until the connection closes to invoke the callbacks).
+ #
+ # @private
+ def unbind
+ unless @succeeded
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status(:failed, @return_values)
+ end
+ end
+
+ # @private
+ def receive_line ln
+ $>.puts ln if @args[:verbose]
+ @range = ln[0...1].to_i
+ @code = ln[0...3].to_i
+ @msg << ln[4..-1]
+ unless ln[3...4] == '-'
+ $>.puts @responder if @args[:verbose]
+ send @responder
+ @msg.clear
+ end
+ end
+
+ private
+
+ # We encountered an error from the server and will close the connection.
+ # Use the error and message the server returned.
+ #
+ def invoke_error
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status :failed, @return_values
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ end
+
+ # We encountered an error on our side of the protocol and will close the connection.
+ # Use an extra-protocol error code (900) and use the message from the caller.
+ #
+ def invoke_internal_error msg = "???"
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = 900
+ @return_values.message = msg
+ set_deferred_status :failed, @return_values
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ end
+
+ def send_ehlo
+ send_data "EHLO #{@args[:domain]}\r\n"
+ end
+
+ def receive_signon
+ return invoke_error unless @range == 2
+ send_ehlo
+ @responder = :receive_ehlo_response
+ end
+ def receive_ehlo_response
+ return invoke_error unless @range == 2
+ @server_caps = @msg
+ invoke_starttls
+ end
+
+ def invoke_starttls
+ if @args[:starttls]
+ # It would be more sociable to first ask if @server_caps contains
+ # the string "STARTTLS" before we invoke it, but hey, life's too short.
+ send_data "STARTTLS\r\n"
+ @responder = :receive_starttls_response
+ else
+ invoke_auth
+ end
+ end
+ def receive_starttls_response
+ return invoke_error unless @range == 2
+ start_tls
+ invoke_ehlo_over_tls
+ end
+
+ def invoke_ehlo_over_tls
+ send_ehlo
+ @responder = :receive_ehlo_over_tls_response
+ end
+ def receive_ehlo_over_tls_response
+ return invoke_error unless @range == 2
+ invoke_auth
+ end
+
+ # Perform an authentication. If the caller didn't request one, then fall through
+ # to the mail-from state.
+ def invoke_auth
+ if @args[:auth]
+ if @args[:auth][:type] == :plain
+ psw = @args[:auth][:password]
+ if psw.respond_to?(:call)
+ psw = psw.call
+ end
+ #str = Base64::encode64("\0#{@args[:auth][:username]}\0#{psw}").chomp
+ str = ["\0#{@args[:auth][:username]}\0#{psw}"].pack("m").gsub(/\n/, '')
+ send_data "AUTH PLAIN #{str}\r\n"
+ @responder = :receive_auth_response
+ else
+ return invoke_internal_error("unsupported auth type")
+ end
+ else
+ invoke_mail_from
+ end
+ end
+ def receive_auth_response
+ return invoke_error unless @range == 2
+ invoke_mail_from
+ end
+
+ def invoke_mail_from
+ send_data "MAIL FROM: <#{@args[:from]}>\r\n"
+ @responder = :receive_mail_from_response
+ end
+ def receive_mail_from_response
+ return invoke_error unless @range == 2
+ invoke_rcpt_to
+ end
+
+ def invoke_rcpt_to
+ @rcpt_responses ||= []
+ l = @rcpt_responses.length
+ to = @args[:to].is_a?(Array) ? @args[:to] : [@args[:to].to_s]
+ if l < to.length
+ send_data "RCPT TO: <#{to[l]}>\r\n"
+ @responder = :receive_rcpt_to_response
+ else
+ e = @rcpt_responses.select {|rr| rr.last == 2}
+ if e and e.length > 0
+ invoke_data
+ else
+ invoke_error
+ end
+ end
+ end
+ def receive_rcpt_to_response
+ @rcpt_responses << [@code, @msg, @range]
+ invoke_rcpt_to
+ end
+
+ def escape_leading_dots(s)
+ s.gsub(/^\./, '..')
+ end
+
+ def invoke_data
+ send_data "DATA\r\n"
+ @responder = :receive_data_response
+ end
+ def receive_data_response
+ return invoke_error unless @range == 3
+
+ # The data to send can be given in either @args[:message], @args[:content], or the
+ # combination of @args[:header] and @args[:body].
+ #
+ # - @args[:message] (String) MUST be a valid RFC2822 Internet Message
+ #
+ # - @args[:content] (String) MUST be in correct SMTP body format, with escaped
+ # leading dots and a trailing dot line
+ #
+ # - @args[:header] (Hash or String)
+ # - @args[:body] (Array or String)
+ if @args[:message]
+ send_data escape_leading_dots(@args[:message].to_s)
+ send_data "\r\n.\r\n"
+ elsif @args[:content]
+ send_data @args[:content].to_s
+ else
+ # The header can be a hash or an array.
+ if @args[:header].is_a?(Hash)
+ (@args[:header] || {}).each {|k,v| send_data escape_leading_dots("#{k}: #{v}\r\n") }
+ else
+ send_data escape_leading_dots(@args[:header].to_s)
+ end
+ send_data "\r\n"
+
+ if @args[:body].is_a?(Array)
+ @args[:body].each {|e| send_data escape_leading_dots(e)}
+ else
+ send_data escape_leading_dots(@args[:body].to_s)
+ end
+
+ send_data "\r\n.\r\n"
+ end
+
+ @responder = :receive_message_response
+ end
+ def receive_message_response
+ return invoke_error unless @range == 2
+ send_data "QUIT\r\n"
+ close_connection_after_writing
+ @succeeded = true
+ @return_values.elapsed_time = Time.now - @return_values.start_time
+ @return_values.responder = @responder
+ @return_values.code = @code
+ @return_values.message = @msg
+ set_deferred_status :succeeded, @return_values
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb
new file mode 100644
index 0000000..e0f1f05
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/smtpserver.rb
@@ -0,0 +1,666 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # This is a protocol handler for the server side of SMTP.
+ # It's NOT a complete SMTP server obeying all the semantics of servers conforming to
+ # RFC2821. Rather, it uses overridable method stubs to communicate protocol states
+ # and data to user code. User code is responsible for doing the right things with the
+ # data in order to get complete and correct SMTP server behavior.
+ #
+ # Simple SMTP server example:
+ #
+ # class EmailServer < EM::P::SmtpServer
+ # def receive_plain_auth(user, pass)
+ # true
+ # end
+ #
+ # def get_server_domain
+ # "mock.smtp.server.local"
+ # end
+ #
+ # def get_server_greeting
+ # "mock smtp server greets you with impunity"
+ # end
+ #
+ # def receive_sender(sender)
+ # current.sender = sender
+ # true
+ # end
+ #
+ # def receive_recipient(recipient)
+ # current.recipient = recipient
+ # true
+ # end
+ #
+ # def receive_message
+ # current.received = true
+ # current.completed_at = Time.now
+ #
+ # p [:received_email, current]
+ # @current = OpenStruct.new
+ # true
+ # end
+ #
+ # def receive_ehlo_domain(domain)
+ # @ehlo_domain = domain
+ # true
+ # end
+ #
+ # def receive_data_command
+ # current.data = ""
+ # true
+ # end
+ #
+ # def receive_data_chunk(data)
+ # current.data << data.join("\n")
+ # true
+ # end
+ #
+ # def receive_transaction
+ # if @ehlo_domain
+ # current.ehlo_domain = @ehlo_domain
+ # @ehlo_domain = nil
+ # end
+ # true
+ # end
+ #
+ # def current
+ # @current ||= OpenStruct.new
+ # end
+ #
+ # def self.start(host = 'localhost', port = 1025)
+ # require 'ostruct'
+ # @server = EM.start_server host, port, self
+ # end
+ #
+ # def self.stop
+ # if @server
+ # EM.stop_server @server
+ # @server = nil
+ # end
+ # end
+ #
+ # def self.running?
+ # !!@server
+ # end
+ # end
+ #
+ # EM.run{ EmailServer.start }
+ #
+ #--
+ # Useful paragraphs in RFC-2821:
+ # 4.3.2: Concise list of command-reply sequences, in essence a text representation
+ # of the command state-machine.
+ #
+ # STARTTLS is defined in RFC2487.
+ # Observe that there are important rules governing whether a publicly-referenced server
+ # (meaning one whose Internet address appears in public MX records) may require the
+ # non-optional use of TLS.
+ # Non-optional TLS does not apply to EHLO, NOOP, QUIT or STARTTLS.
+ class SmtpServer < EventMachine::Connection
+ include Protocols::LineText2
+
+ HeloRegex = /\AHELO\s*/i
+ EhloRegex = /\AEHLO\s*/i
+ QuitRegex = /\AQUIT/i
+ MailFromRegex = /\AMAIL FROM:\s*/i
+ RcptToRegex = /\ARCPT TO:\s*/i
+ DataRegex = /\ADATA/i
+ NoopRegex = /\ANOOP/i
+ RsetRegex = /\ARSET/i
+ VrfyRegex = /\AVRFY\s+/i
+ ExpnRegex = /\AEXPN\s+/i
+ HelpRegex = /\AHELP/i
+ StarttlsRegex = /\ASTARTTLS/i
+ AuthRegex = /\AAUTH\s+/i
+
+
+ # Class variable containing default parameters that can be overridden
+ # in application code.
+ # Individual objects of this class will make an instance-local copy of
+ # the class variable, so that they can be reconfigured on a per-instance
+ # basis.
+ #
+ # Chunksize is the number of data lines we'll buffer before
+ # sending them to the application. TODO, make this user-configurable.
+ #
+ @@parms = {
+ :chunksize => 4000,
+ :verbose => false
+ }
+ def self.parms= parms={}
+ @@parms.merge!(parms)
+ end
+
+
+
+ def initialize *args
+ super
+ @parms = @@parms
+ init_protocol_state
+ end
+
+ def parms= parms={}
+ @parms.merge!(parms)
+ end
+
+ # In SMTP, the server talks first. But by a (perhaps flawed) axiom in EM,
+ # #post_init will execute BEFORE the block passed to #start_server, for any
+ # given accepted connection. Since in this class we'll probably be getting
+ # a lot of initialization parameters, we want the guts of post_init to
+ # run AFTER the application has initialized the connection object. So we
+ # use a spawn to schedule the post_init to run later.
+ # It's a little weird, I admit. A reasonable alternative would be to set
+ # parameters as a class variable and to do that before accepting any connections.
+ #
+ # OBSOLETE, now we have @@parms. But the spawn is nice to keep as an illustration.
+ #
+ def post_init
+ #send_data "220 #{get_server_greeting}\r\n" (ORIGINAL)
+ #(EM.spawn {|x| x.send_data "220 #{x.get_server_greeting}\r\n"}).notify(self)
+ (EM.spawn {|x| x.send_server_greeting}).notify(self)
+ end
+
+ def send_server_greeting
+ send_data "220 #{get_server_greeting}\r\n"
+ end
+
+ def receive_line ln
+ @@parms[:verbose] and $>.puts ">>> #{ln}"
+
+ return process_data_line(ln) if @state.include?(:data)
+ return process_auth_line(ln) if @state.include?(:auth_incomplete)
+
+ case ln
+ when EhloRegex
+ process_ehlo $'.dup
+ when HeloRegex
+ process_helo $'.dup
+ when MailFromRegex
+ process_mail_from $'.dup
+ when RcptToRegex
+ process_rcpt_to $'.dup
+ when DataRegex
+ process_data
+ when RsetRegex
+ process_rset
+ when VrfyRegex
+ process_vrfy
+ when ExpnRegex
+ process_expn
+ when HelpRegex
+ process_help
+ when NoopRegex
+ process_noop
+ when QuitRegex
+ process_quit
+ when StarttlsRegex
+ process_starttls
+ when AuthRegex
+ process_auth $'.dup
+ else
+ process_unknown
+ end
+ end
+
+ # TODO - implement this properly, the implementation is a stub!
+ def process_help
+ send_data "250 Ok, but unimplemented\r\n"
+ end
+
+ # RFC2821, 3.5.3 Meaning of VRFY or EXPN Success Response:
+ # A server MUST NOT return a 250 code in response to a VRFY or EXPN
+ # command unless it has actually verified the address. In particular,
+ # a server MUST NOT return 250 if all it has done is to verify that the
+ # syntax given is valid. In that case, 502 (Command not implemented)
+ # or 500 (Syntax error, command unrecognized) SHOULD be returned.
+ #
+ # TODO - implement this properly, the implementation is a stub!
+ def process_vrfy
+ send_data "502 Command not implemented\r\n"
+ end
+ # TODO - implement this properly, the implementation is a stub!
+ def process_expn
+ send_data "502 Command not implemented\r\n"
+ end
+
+ #--
+ # This is called at several points to restore the protocol state
+ # to a pre-transaction state. In essence, we "forget" having seen
+ # any valid command except EHLO and STARTTLS.
+ # We also have to callback user code, in case they're keeping track
+ # of senders, recipients, and whatnot.
+ #
+ # We try to follow the convention of avoiding the verb "receive" for
+ # internal method names except receive_line (which we inherit), and
+ # using only receive_xxx for user-overridable stubs.
+ #
+ # init_protocol_state is called when we initialize the connection as
+ # well as during reset_protocol_state. It does NOT call the user
+ # override method. This enables us to promise the users that they
+ # won't see the overridable fire except after EHLO and RSET, and
+ # after a message has been received. Although the latter may be wrong.
+ # The standard may allow multiple DATA segments with the same set of
+ # senders and recipients.
+ #
+ def reset_protocol_state
+ init_protocol_state
+ s,@state = @state,[]
+ @state << :starttls if s.include?(:starttls)
+ @state << :ehlo if s.include?(:ehlo)
+ receive_transaction
+ end
+ def init_protocol_state
+ @state ||= []
+ end
+
+
+ #--
+ # EHLO/HELO is always legal, per the standard. On success
+ # it always clears buffers and initiates a mail "transaction."
+ # Which means that a MAIL FROM must follow.
+ #
+ # Per the standard, an EHLO/HELO or a RSET "initiates" an email
+ # transaction. Thereafter, MAIL FROM must be received before
+ # RCPT TO, before DATA. Not sure what this specific ordering
+ # achieves semantically, but it does make it easier to
+ # implement. We also support user-specified requirements for
+ # STARTTLS and AUTH. We make it impossible to proceed to MAIL FROM
+ # without fulfilling tls and/or auth, if the user specified either
+ # or both as required. We need to check the extension standard
+ # for auth to see if a credential is discarded after a RSET along
+ # with all the rest of the state. We'll behave as if it is.
+ # Now clearly, we can't discard tls after its been negotiated
+ # without dropping the connection, so that flag doesn't get cleared.
+ #
+ def process_ehlo domain
+ if receive_ehlo_domain domain
+ send_data "250-#{get_server_domain}\r\n"
+ if @@parms[:starttls]
+ send_data "250-STARTTLS\r\n"
+ end
+ if @@parms[:auth]
+ send_data "250-AUTH PLAIN\r\n"
+ end
+ send_data "250-NO-SOLICITING\r\n"
+ # TODO, size needs to be configurable.
+ send_data "250 SIZE 20000000\r\n"
+ reset_protocol_state
+ @state << :ehlo
+ else
+ send_data "550 Requested action not taken\r\n"
+ end
+ end
+
+ def process_helo domain
+ if receive_ehlo_domain domain.dup
+ send_data "250 #{get_server_domain}\r\n"
+ reset_protocol_state
+ @state << :ehlo
+ else
+ send_data "550 Requested action not taken\r\n"
+ end
+ end
+
+ def process_quit
+ send_data "221 Ok\r\n"
+ close_connection_after_writing
+ end
+
+ def process_noop
+ send_data "250 Ok\r\n"
+ end
+
+ def process_unknown
+ send_data "500 Unknown command\r\n"
+ end
+
+ #--
+ # So far, only AUTH PLAIN is supported but we should do at least LOGIN as well.
+ # TODO, support clients that send AUTH PLAIN with no parameter, expecting a 3xx
+ # response and a continuation of the auth conversation.
+ #
+ def process_auth str
+ if @state.include?(:auth)
+ send_data "503 auth already issued\r\n"
+ elsif str =~ /\APLAIN\s?/i
+ if $'.length == 0
+ # we got a partial response, so let the client know to send the rest
+ @state << :auth_incomplete
+ send_data("334 \r\n")
+ else
+ # we got the initial response, so go ahead & process it
+ process_auth_line($')
+ end
+ #elsif str =~ /\ALOGIN\s+/i
+ else
+ send_data "504 auth mechanism not available\r\n"
+ end
+ end
+
+ def process_auth_line(line)
+ plain = line.unpack("m").first
+ _,user,psw = plain.split("\000")
+
+ succeeded = proc {
+ send_data "235 authentication ok\r\n"
+ @state << :auth
+ }
+ failed = proc {
+ send_data "535 invalid authentication\r\n"
+ }
+ auth = receive_plain_auth user,psw
+
+ if auth.respond_to?(:callback)
+ auth.callback(&succeeded)
+ auth.errback(&failed)
+ else
+ (auth ? succeeded : failed).call
+ end
+
+ @state.delete :auth_incomplete
+ end
+
+ #--
+ # Unusually, we can deal with a Deferrable returned from the user application.
+ # This was added to deal with a special case in a particular application, but
+ # it would be a nice idea to add it to the other user-code callbacks.
+ #
+ def process_data
+ unless @state.include?(:rcpt)
+ send_data "503 Operation sequence error\r\n"
+ else
+ succeeded = proc {
+ send_data "354 Send it\r\n"
+ @state << :data
+ @databuffer = []
+ }
+ failed = proc {
+ send_data "550 Operation failed\r\n"
+ }
+
+ d = receive_data_command
+
+ if d.respond_to?(:callback)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+ end
+ end
+
+ def process_rset
+ reset_protocol_state
+ receive_reset
+ send_data "250 Ok\r\n"
+ end
+
+ def unbind
+ connection_ended
+ end
+
+ #--
+ # STARTTLS may not be issued before EHLO, or unless the user has chosen
+ # to support it.
+ #
+ # If :starttls_options is present and :starttls is set in the parms
+ # pass the options in :starttls_options to start_tls. Do this if you want to use
+ # your own certificate
+ # e.g. {:cert_chain_file => "/etc/ssl/cert.pem", :private_key_file => "/etc/ssl/private/cert.key"}
+
+ def process_starttls
+ if @@parms[:starttls]
+ if @state.include?(:starttls)
+ send_data "503 TLS Already negotiated\r\n"
+ elsif ! @state.include?(:ehlo)
+ send_data "503 EHLO required before STARTTLS\r\n"
+ else
+ send_data "220 Start TLS negotiation\r\n"
+ start_tls(@@parms[:starttls_options] || {})
+ @state << :starttls
+ end
+ else
+ process_unknown
+ end
+ end
+
+
+ #--
+ # Requiring TLS is touchy, cf RFC2784.
+ # Requiring AUTH seems to be much more reasonable.
+ # We don't currently support any notion of deriving an authentication from the TLS
+ # negotiation, although that would certainly be reasonable.
+ # We DON'T allow MAIL FROM to be given twice.
+ # We DON'T enforce all the various rules for validating the sender or
+ # the reverse-path (like whether it should be null), and notifying the reverse
+ # path in case of delivery problems. All of that is left to the calling application.
+ #
+ def process_mail_from sender
+ if (@@parms[:starttls]==:required and !@state.include?(:starttls))
+ send_data "550 This server requires STARTTLS before MAIL FROM\r\n"
+ elsif (@@parms[:auth]==:required and !@state.include?(:auth))
+ send_data "550 This server requires authentication before MAIL FROM\r\n"
+ elsif @state.include?(:mail_from)
+ send_data "503 MAIL already given\r\n"
+ else
+ unless receive_sender sender
+ send_data "550 sender is unacceptable\r\n"
+ else
+ send_data "250 Ok\r\n"
+ @state << :mail_from
+ end
+ end
+ end
+
+ #--
+ # Since we require :mail_from to have been seen before we process RCPT TO,
+ # we don't need to repeat the tests for TLS and AUTH.
+ # Note that we don't remember or do anything else with the recipients.
+ # All of that is on the user code.
+ # TODO: we should enforce user-definable limits on the total number of
+ # recipients per transaction.
+ # We might want to make sure that a given recipient is only seen once, but
+ # for now we'll let that be the user's problem.
+ #
+ # User-written code can return a deferrable from receive_recipient.
+ #
+ def process_rcpt_to rcpt
+ unless @state.include?(:mail_from)
+ send_data "503 MAIL is required before RCPT\r\n"
+ else
+ succeeded = proc {
+ send_data "250 Ok\r\n"
+ @state << :rcpt unless @state.include?(:rcpt)
+ }
+ failed = proc {
+ send_data "550 recipient is unacceptable\r\n"
+ }
+
+ d = receive_recipient rcpt
+
+ if d.respond_to?(:set_deferred_status)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+
+=begin
+ unless receive_recipient rcpt
+ send_data "550 recipient is unacceptable\r\n"
+ else
+ send_data "250 Ok\r\n"
+ @state << :rcpt unless @state.include?(:rcpt)
+ end
+=end
+ end
+ end
+
+
+ # Send the incoming data to the application one chunk at a time, rather than
+ # one line at a time. That lets the application be a little more flexible about
+ # storing to disk, etc.
+ # Since we clear the chunk array every time we submit it, the caller needs to be
+ # aware to do things like dup it if he wants to keep it around across calls.
+ #
+ # Resets the transaction upon disposition of the incoming message.
+ # RFC5321 says this about the MAIL FROM command:
+ # "This command tells the SMTP-receiver that a new mail transaction is
+ # starting and to reset all its state tables and buffers, including any
+ # recipients or mail data."
+ #
+ # Equivalent behaviour is implemented by resetting after a completed transaction.
+ #
+ # User-written code can return a Deferrable as a response from receive_message.
+ #
+ def process_data_line ln
+ if ln == "."
+ if @databuffer.length > 0
+ receive_data_chunk @databuffer
+ @databuffer.clear
+ end
+
+
+ succeeded = proc {
+ send_data "250 Message accepted\r\n"
+ reset_protocol_state
+ }
+ failed = proc {
+ send_data "550 Message rejected\r\n"
+ reset_protocol_state
+ }
+ d = receive_message
+
+ if d.respond_to?(:set_deferred_status)
+ d.callback(&succeeded)
+ d.errback(&failed)
+ else
+ (d ? succeeded : failed).call
+ end
+
+ @state.delete :data
+ else
+ # slice off leading . if any
+ ln.slice!(0...1) if ln[0] == ?.
+ @databuffer << ln
+ if @databuffer.length > @@parms[:chunksize]
+ receive_data_chunk @databuffer
+ @databuffer.clear
+ end
+ end
+ end
+
+
+ #------------------------------------------
+ # Everything from here on can be overridden in user code.
+
+ # The greeting returned in the initial connection message to the client.
+ def get_server_greeting
+ "EventMachine SMTP Server"
+ end
+ # The domain name returned in the first line of the response to a
+ # successful EHLO or HELO command.
+ def get_server_domain
+ "Ok EventMachine SMTP Server"
+ end
+
+ # A false response from this user-overridable method will cause a
+ # 550 error to be returned to the remote client.
+ #
+ def receive_ehlo_domain domain
+ true
+ end
+
+ # Return true or false to indicate that the authentication is acceptable.
+ def receive_plain_auth user, password
+ true
+ end
+
+ # Receives the argument of the MAIL FROM command. Return false to
+ # indicate to the remote client that the sender is not accepted.
+ # This can only be successfully called once per transaction.
+ #
+ def receive_sender sender
+ true
+ end
+
+ # Receives the argument of a RCPT TO command. Can be given multiple
+ # times per transaction. Return false to reject the recipient.
+ #
+ def receive_recipient rcpt
+ true
+ end
+
+ # Sent when the remote peer issues the RSET command.
+ # Since RSET is not allowed to fail (according to the protocol),
+ # we ignore any return value from user overrides of this method.
+ #
+ def receive_reset
+ end
+
+ # Sent when the remote peer has ended the connection.
+ #
+ def connection_ended
+ end
+
+ # Called when the remote peer sends the DATA command.
+ # Returning false will cause us to send a 550 error to the peer.
+ # This can be useful for dealing with problems that arise from processing
+ # the whole set of sender and recipients.
+ #
+ def receive_data_command
+ true
+ end
+
+ # Sent when data from the remote peer is available. The size can be controlled
+ # by setting the :chunksize parameter. This call can be made multiple times.
+ # The goal is to strike a balance between sending the data to the application one
+ # line at a time, and holding all of a very large message in memory.
+ #
+ def receive_data_chunk data
+ @smtps_msg_size ||= 0
+ @smtps_msg_size += data.join.length
+ STDERR.write "<#{@smtps_msg_size}>"
+ end
+
+ # Sent after a message has been completely received. User code
+ # must return true or false to indicate whether the message has
+ # been accepted for delivery.
+ def receive_message
+ @@parms[:verbose] and $>.puts "Received complete message"
+ true
+ end
+
+ # This is called when the protocol state is reset. It happens
+ # when the remote client calls EHLO/HELO or RSET.
+ def receive_transaction
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb
new file mode 100644
index 0000000..132f320
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/socks4.rb
@@ -0,0 +1,66 @@
+module EventMachine
+ module Protocols
+ # Basic SOCKS v4 client implementation
+ #
+ # Use as you would any regular connection:
+ #
+ # class MyConn < EM::P::Socks4
+ # def post_init
+ # send_data("sup")
+ # end
+ #
+ # def receive_data(data)
+ # send_data("you said: #{data}")
+ # end
+ # end
+ #
+ # EM.connect socks_host, socks_port, MyConn, host, port
+ #
+ class Socks4 < Connection
+ def initialize(host, port)
+ @host = Socket.gethostbyname(host).last
+ @port = port
+ @socks_error_code = nil
+ @buffer = ''
+ setup_methods
+ end
+
+ def setup_methods
+ class << self
+ def post_init; socks_post_init; end
+ def receive_data(*a); socks_receive_data(*a); end
+ end
+ end
+
+ def restore_methods
+ class << self
+ remove_method :post_init
+ remove_method :receive_data
+ end
+ end
+
+ def socks_post_init
+ header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C")
+ send_data(header)
+ end
+
+ def socks_receive_data(data)
+ @buffer << data
+ return if @buffer.size < 8
+
+ header_resp = @buffer.slice! 0, 8
+ _, r = header_resp.unpack("cc")
+ if r != 90
+ @socks_error_code = r
+ close_connection
+ return
+ end
+
+ restore_methods
+
+ post_init
+ receive_data(@buffer) unless @buffer.empty?
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb
new file mode 100644
index 0000000..ca6f078
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/stomp.rb
@@ -0,0 +1,205 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 15 November 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).
+ #
+ # == Usage example
+ #
+ # module StompClient
+ # include EM::Protocols::Stomp
+ #
+ # def connection_completed
+ # connect :login => 'guest', :passcode => 'guest'
+ # end
+ #
+ # def receive_msg msg
+ # if msg.command == "CONNECTED"
+ # subscribe '/some/topic'
+ # else
+ # p ['got a message', msg]
+ # puts msg.body
+ # end
+ # end
+ # end
+ #
+ # EM.run{
+ # EM.connect 'localhost', 61613, StompClient
+ # }
+ #
+ module Stomp
+ include LineText2
+
+ class Message
+ # The command associated with the message, usually 'CONNECTED' or 'MESSAGE'
+ attr_accessor :command
+ # Hash containing headers such as destination and message-id
+ attr_accessor :header
+ alias :headers :header
+ # Body of the message
+ attr_accessor :body
+
+ # @private
+ def initialize
+ @header = {}
+ @state = :precommand
+ @content_length = nil
+ end
+ # @private
+ def consume_line line
+ if @state == :precommand
+ unless line =~ /\A\s*\Z/
+ @command = line
+ @state = :headers
+ end
+ elsif @state == :headers
+ if line == ""
+ if @content_length
+ yield( [:sized_text, @content_length+1] )
+ else
+ @state = :body
+ yield( [:unsized_text] )
+ end
+ elsif line =~ /\A([^:]+):(.+)\Z/
+ k = $1.dup.strip
+ v = $2.dup.strip
+ @header[k] = v
+ if k == "content-length"
+ @content_length = v.to_i
+ end
+ else
+ # This is a protocol error. How to signal it?
+ end
+ elsif @state == :body
+ @body = line
+ yield( [:dispatch] )
+ end
+ end
+ end
+
+ # @private
+ def send_frame verb, headers={}, body=""
+ body = body.to_s
+ ary = [verb, "\n"]
+ body_bytesize = body.bytesize if body.respond_to? :bytesize
+ body_bytesize ||= body.size
+ headers.each {|k,v| ary << "#{k}:#{v}\n" }
+ ary << "content-length: #{body_bytesize}\n"
+ ary << "content-type: text/plain; charset=UTF-8\n" unless headers.has_key? 'content-type'
+ ary << "\n"
+ ary << body
+ ary << "\0"
+ send_data ary.join
+ end
+
+ # @private
+ def receive_line line
+ @stomp_initialized || init_message_reader
+ @stomp_message.consume_line(line) {|outcome|
+ if outcome.first == :sized_text
+ set_text_mode outcome[1]
+ elsif outcome.first == :unsized_text
+ set_delimiter "\0"
+ elsif outcome.first == :dispatch
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
+ init_message_reader
+ end
+ }
+ end
+
+ # @private
+ def receive_binary_data data
+ @stomp_message.body = data[0..-2]
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
+ init_message_reader
+ end
+
+ # @private
+ def init_message_reader
+ @stomp_initialized = true
+ set_delimiter "\n"
+ set_line_mode
+ @stomp_message = Message.new
+ end
+
+ # Invoked with an incoming Stomp::Message received from the STOMP server
+ def receive_msg msg
+ # stub, overwrite this in your handler
+ end
+
+ # CONNECT command, for authentication
+ #
+ # connect :login => 'guest', :passcode => 'guest'
+ #
+ def connect parms={}
+ send_frame "CONNECT", parms
+ end
+
+ # SEND command, for publishing messages to a topic
+ #
+ # send '/topic/name', 'some message here'
+ #
+ def send destination, body, parms={}
+ send_frame "SEND", parms.merge( :destination=>destination ), body.to_s
+ end
+
+ # SUBSCRIBE command, for subscribing to topics
+ #
+ # subscribe '/topic/name', false
+ #
+ def subscribe dest, ack=false
+ send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}
+ end
+
+ # ACK command, for acknowledging receipt of messages
+ #
+ # module StompClient
+ # include EM::P::Stomp
+ #
+ # def connection_completed
+ # connect :login => 'guest', :passcode => 'guest'
+ # # subscribe with ack mode
+ # subscribe '/some/topic', true
+ # end
+ #
+ # def receive_msg msg
+ # if msg.command == "MESSAGE"
+ # ack msg.headers['message-id']
+ # puts msg.body
+ # end
+ # end
+ # end
+ #
+ def ack msgid
+ send_frame "ACK", 'message-id'=> msgid
+ end
+
+ end
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb
new file mode 100644
index 0000000..3187893
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/protocols/tcptest.rb
@@ -0,0 +1,54 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 16 July 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+#
+
+module EventMachine
+ module Protocols
+
+ # @private
+ class TcpConnectTester < Connection
+ include EventMachine::Deferrable
+
+ def self.test( host, port )
+ EventMachine.connect( host, port, self )
+ end
+
+ def post_init
+ @start_time = Time.now
+ end
+
+ def connection_completed
+ @completed = true
+ set_deferred_status :succeeded, (Time.now - @start_time)
+ close_connection
+ end
+
+ def unbind
+ set_deferred_status :failed, (Time.now - @start_time) unless @completed
+ end
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pure_ruby.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pure_ruby.rb
new file mode 100644
index 0000000..94fd175
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/pure_ruby.rb
@@ -0,0 +1,1284 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 8 Apr 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#-------------------------------------------------------------------
+#
+#
+
+# TODO List:
+# TCP-connects currently assume non-blocking connect is available- need to
+# degrade automatically on versions of Ruby prior to June 2006.
+#
+
+require 'singleton'
+require 'forwardable'
+require 'socket'
+require 'fcntl'
+require 'set'
+require 'openssl'
+
+module EventMachine
+ # @private
+ class Error < Exception; end
+ # @private
+ class UnknownTimerFired < RuntimeError; end
+ # @private
+ class Unsupported < RuntimeError; end
+ # @private
+ class ConnectionError < RuntimeError; end
+ # @private
+ class ConnectionNotBound < RuntimeError; end
+
+ # Older versions of Ruby may not provide the SSLErrorWaitReadable
+ # OpenSSL class. Create an error class to act as a "proxy".
+ if defined?(OpenSSL::SSL::SSLErrorWaitReadable)
+ SSLConnectionWaitReadable = OpenSSL::SSL::SSLErrorWaitReadable
+ else
+ SSLConnectionWaitReadable = IO::WaitReadable
+ end
+
+ # Older versions of Ruby may not provide the SSLErrorWaitWritable
+ # OpenSSL class. Create an error class to act as a "proxy".
+ if defined?(OpenSSL::SSL::SSLErrorWaitWritable)
+ SSLConnectionWaitWritable = OpenSSL::SSL::SSLErrorWaitWritable
+ else
+ SSLConnectionWaitWritable = IO::WaitWritable
+ end
+end
+
+module EventMachine
+ class CertificateCreator
+ attr_reader :cert, :key
+
+ def initialize
+ @key = OpenSSL::PKey::RSA.new(1024)
+ public_key = @key.public_key
+ subject = "/C=EventMachine/O=EventMachine/OU=EventMachine/CN=EventMachine"
+ @cert = OpenSSL::X509::Certificate.new
+ @cert.subject = @cert.issuer = OpenSSL::X509::Name.parse(subject)
+ @cert.not_before = Time.now
+ @cert.not_after = Time.now + 365 * 24 * 60 * 60
+ @cert.public_key = public_key
+ @cert.serial = 0x0
+ @cert.version = 2
+ factory = OpenSSL::X509::ExtensionFactory.new
+ factory.subject_certificate = @cert
+ factory.issuer_certificate = @cert
+ @cert.extensions = [
+ factory.create_extension("basicConstraints","CA:TRUE", true),
+ factory.create_extension("subjectKeyIdentifier", "hash")
+ ]
+ @cert.add_extension factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
+ @cert.sign(@key, OpenSSL::Digest::SHA1.new)
+ end
+ end
+
+ # @private
+ DefaultCertificate = CertificateCreator.new
+
+ # @private
+ DefaultDHKey1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ
+AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR
+T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC
+-----END DH PARAMETERS-----
+ _end_of_pem_
+
+ # @private
+ DefaultDHKey2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
+JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
+VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
+YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
+1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
+7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
+-----END DH PARAMETERS-----
+ _end_of_pem_
+end
+
+# @private
+module EventMachine
+ class << self
+ # This is mostly useful for automated tests.
+ # Return a distinctive symbol so the caller knows whether he's dealing
+ # with an extension or with a pure-Ruby library.
+ # @private
+ def library_type
+ :pure_ruby
+ end
+
+ # @private
+ def initialize_event_machine
+ Reactor.instance.initialize_for_run
+ end
+
+ # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
+ # processor still wants them in seconds.
+ # @private
+ def add_oneshot_timer interval
+ Reactor.instance.install_oneshot_timer(interval / 1000)
+ end
+
+ # @private
+ def run_machine
+ Reactor.instance.run
+ end
+
+ # @private
+ def release_machine
+ end
+
+
+ def stopping?
+ return Reactor.instance.stop_scheduled
+ end
+
+ # @private
+ def stop
+ Reactor.instance.stop
+ end
+
+ # @private
+ def connect_server host, port
+ bind_connect_server nil, nil, host, port
+ end
+
+ # @private
+ def bind_connect_server bind_addr, bind_port, host, port
+ EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid
+ end
+
+ # @private
+ def send_data target, data, datalength
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
+ selectable.send_data data
+ end
+
+ # @private
+ def close_connection target, after_writing
+ selectable = Reactor.instance.get_selectable( target )
+ selectable.schedule_close after_writing if selectable
+ end
+
+ # @private
+ def start_tcp_server host, port
+ (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
+ s.uuid
+ end
+
+ # @private
+ def stop_tcp_server sig
+ s = Reactor.instance.get_selectable(sig)
+ s.schedule_close
+ end
+
+ # @private
+ def start_unix_server chain
+ (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
+ s.uuid
+ end
+
+ # @private
+ def connect_unix_server chain
+ EvmaUNIXClient.connect(chain).uuid
+ end
+
+ # @private
+ def signal_loopbreak
+ Reactor.instance.signal_loopbreak
+ end
+
+ # @private
+ def get_peername sig
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
+ selectable.get_peername
+ end
+
+ # @private
+ def get_sockname sig
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_sockname target"
+ selectable.get_sockname
+ end
+
+ # @private
+ def open_udp_socket host, port
+ EvmaUDPSocket.create(host, port).uuid
+ end
+
+ # This is currently only for UDP!
+ # We need to make it work with unix-domain sockets as well.
+ # @private
+ def send_datagram target, data, datalength, host, port
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
+ selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)
+ end
+
+
+ # Sets reactor quantum in milliseconds. The underlying Reactor function wants a (possibly
+ # fractional) number of seconds.
+ # @private
+ def set_timer_quantum interval
+ Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
+ end
+
+ # This method is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
+ # that user code behaves properly across different EM implementations.
+ # @private
+ def epoll
+ end
+
+ # @private
+ def ssl?
+ true
+ end
+
+ def tls_parm_set?(parm)
+ !(parm.nil? || parm.empty?)
+ end
+
+ # This method takes a series of positional arguments for specifying such
+ # things as private keys and certificate chains. It's expected that the
+ # parameter list will grow as we add more supported features. ALL of these
+ # parameters are optional, and can be specified as empty or nil strings.
+ # @private
+ def set_tls_parms signature, priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask
+ bitmask = protocols_bitmask
+ ssl_options = OpenSSL::SSL::OP_ALL
+ ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0
+ ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) && EM_PROTO_SSLv3 & bitmask == 0
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0
+ @tls_parms ||= {}
+ @tls_parms[signature] = {
+ :verify_peer => verify_peer,
+ :fail_if_no_peer_cert => fail_if_no_peer_cert,
+ :ssl_options => ssl_options
+ }
+ @tls_parms[signature][:priv_key] = File.read(priv_key) if tls_parm_set?(priv_key)
+ @tls_parms[signature][:cert_chain] = File.read(cert_chain) if tls_parm_set?(cert_chain)
+ @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname)
+ @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list)
+ @tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam)
+ @tls_parms[signature][:ecdh_curve] = ecdh_curve if tls_parm_set?(ecdh_curve)
+ end
+
+ def start_tls signature
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown io selectable for start_tls"
+ tls_parms = @tls_parms[signature]
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.options = tls_parms[:ssl_options]
+ ctx.cert = DefaultCertificate.cert
+ ctx.key = DefaultCertificate.key
+ ctx.cert_store = OpenSSL::X509::Store.new
+ ctx.cert_store.set_default_paths
+ ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain]
+ ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key]
+ verify_mode = OpenSSL::SSL::VERIFY_NONE
+ if tls_parms[:verify_peer]
+ verify_mode |= OpenSSL::SSL::VERIFY_PEER
+ end
+ if tls_parms[:fail_if_no_peer_cert]
+ verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
+ end
+ ctx.verify_mode = verify_mode
+ ctx.servername_cb = Proc.new do |_, server_name|
+ tls_parms[:server_name] = server_name
+ nil
+ end
+ ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list]
+ if selectable.is_server
+ ctx.tmp_dh_callback = Proc.new do |_, _, key_length|
+ if tls_parms[:dhparam]
+ OpenSSL::PKey::DH.new(tls_parms[:dhparam])
+ else
+ case key_length
+ when 1024 then DefaultDHKey1024
+ when 2048 then DefaultDHKey2048
+ else
+ nil
+ end
+ end
+ end
+ if tls_parms[:ecdh_curve] && ctx.respond_to?(:tmp_ecdh_callback)
+ ctx.tmp_ecdh_callback = Proc.new do
+ OpenSSL::PKey::EC.new(tls_parms[:ecdh_curve])
+ end
+ end
+ end
+ ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx)
+ ssl_io.sync_close = true
+ if tls_parms[:sni_hostname]
+ ssl_io.hostname = tls_parms[:sni_hostname] if ssl_io.respond_to?(:hostname=)
+ end
+ begin
+ selectable.is_server ? ssl_io.accept_nonblock : ssl_io.connect_nonblock
+ rescue; end
+ selectable.io = ssl_io
+ end
+
+ def get_peer_cert signature
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_peer_cert target"
+ if selectable.io.respond_to?(:peer_cert) && selectable.io.peer_cert
+ selectable.io.peer_cert.to_pem
+ else
+ nil
+ end
+ end
+
+ def get_cipher_name signature
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_name target"
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[0] : nil
+ end
+
+ def get_cipher_protocol signature
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_protocol target"
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[1] : nil
+ end
+
+ def get_cipher_bits signature
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_bits target"
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[2] : nil
+ end
+
+ def get_sni_hostname signature
+ @tls_parms ||= {}
+ if @tls_parms[signature]
+ @tls_parms[signature][:server_name]
+ else
+ nil
+ end
+ end
+
+ # This method is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
+ # per-process file-descriptor limit.
+ # @private
+ def set_rlimit_nofile n
+ 1024
+ end
+
+ # This method is a harmless no-op in pure Ruby, which doesn't have a built-in limit
+ # on the number of available timers.
+ # @private
+ def set_max_timer_count n
+ end
+
+ # @private
+ def get_sock_opt signature, level, optname
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_sock_opt target"
+ selectable.getsockopt level, optname
+ end
+
+ # @private
+ def set_sock_opt signature, level, optname, optval
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown set_sock_opt target"
+ selectable.setsockopt level, optname, optval
+ end
+
+ # @private
+ def send_file_data sig, filename
+ sz = File.size(filename)
+ raise "file too large" if sz > 32*1024
+ data =
+ begin
+ File.read filename
+ rescue
+ ""
+ end
+ send_data sig, data, data.length
+ end
+
+ # @private
+ def get_outbound_data_size sig
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
+ r.get_outbound_data_size
+ end
+
+ # @private
+ def read_keyboard
+ EvmaKeyboard.open.uuid
+ end
+
+ # @private
+ def set_comm_inactivity_timeout sig, tm
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
+ r.set_inactivity_timeout tm
+ end
+
+ # @private
+ def set_pending_connect_timeout sig, tm
+ # Needs to be implemented. Currently a no-op stub to allow
+ # certain software to operate with the EM pure-ruby.
+ end
+
+ # @private
+ def report_connection_error_status signature
+ get_sock_opt(signature, Socket::SOL_SOCKET, Socket::SO_ERROR).int
+ end
+ end
+end
+
+module EventMachine
+ # @private
+ class Connection
+ # @private
+ def get_outbound_data_size
+ EventMachine::get_outbound_data_size @signature
+ end
+ end
+end
+
+module EventMachine
+
+ # Factored out so we can substitute other implementations
+ # here if desired, such as the one in ActiveRBAC.
+ # @private
+ module UuidGenerator
+ def self.generate
+ @ix ||= 0
+ @ix += 1
+ end
+ end
+end
+
+
+module EventMachine
+ # @private
+ TimerFired = 100
+ # @private
+ ConnectionData = 101
+ # @private
+ ConnectionUnbound = 102
+ # @private
+ ConnectionAccepted = 103
+ # @private
+ ConnectionCompleted = 104
+ # @private
+ LoopbreakSignalled = 105
+ # @private
+ ConnectionNotifyReadable = 106
+ # @private
+ ConnectionNotifyWritable = 107
+ # @private
+ SslHandshakeCompleted = 108
+ # @private
+ SslVerify = 109
+ # @private
+ EM_PROTO_SSLv2 = 2
+ # @private
+ EM_PROTO_SSLv3 = 4
+ # @private
+ EM_PROTO_TLSv1 = 8
+ # @private
+ EM_PROTO_TLSv1_1 = 16
+ # @private
+ EM_PROTO_TLSv1_2 = 32
+end
+
+module EventMachine
+ # @private
+ class Reactor
+ include Singleton
+
+ HeartbeatInterval = 2
+
+ attr_reader :current_loop_time, :stop_scheduled
+
+ def initialize
+ initialize_for_run
+ end
+
+ def install_oneshot_timer interval
+ uuid = UuidGenerator::generate
+ #@timers << [Time.now + interval, uuid]
+ #@timers.sort! {|a,b| a.first <=> b.first}
+ @timers.add([Time.now + interval, uuid])
+ uuid
+ end
+
+ # Called before run, this is a good place to clear out arrays
+ # with cruft that may be left over from a previous run.
+ # @private
+ def initialize_for_run
+ @running = false
+ @stop_scheduled = false
+ @selectables ||= {}; @selectables.clear
+ @timers = SortedSet.new # []
+ set_timer_quantum(0.1)
+ @current_loop_time = Time.now
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
+ end
+
+ def add_selectable io
+ @selectables[io.uuid] = io
+ end
+
+ def get_selectable uuid
+ @selectables[uuid]
+ end
+
+ def run
+ raise Error.new( "already running" ) if @running
+ @running = true
+
+ begin
+ open_loopbreaker
+
+ loop {
+ @current_loop_time = Time.now
+
+ break if @stop_scheduled
+ run_timers
+ break if @stop_scheduled
+ crank_selectables
+ break if @stop_scheduled
+ run_heartbeats
+ }
+ ensure
+ close_loopbreaker
+ @selectables.each {|k, io| io.close}
+ @selectables.clear
+
+ @running = false
+ end
+
+ end
+
+ def run_timers
+ @timers.each {|t|
+ if t.first <= @current_loop_time
+ @timers.delete t
+ EventMachine::event_callback "", TimerFired, t.last
+ else
+ break
+ end
+ }
+ #while @timers.length > 0 and @timers.first.first <= now
+ # t = @timers.shift
+ # EventMachine::event_callback "", TimerFired, t.last
+ #end
+ end
+
+ def run_heartbeats
+ if @next_heartbeat <= @current_loop_time
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
+ @selectables.each {|k,io| io.heartbeat}
+ end
+ end
+
+ def crank_selectables
+ #$stderr.write 'R'
+
+ readers = @selectables.values.select {|io| io.select_for_reading?}
+ writers = @selectables.values.select {|io| io.select_for_writing?}
+
+ s = select( readers, writers, nil, @timer_quantum)
+
+ s and s[1] and s[1].each {|w| w.eventable_write }
+ s and s[0] and s[0].each {|r| r.eventable_read }
+
+ @selectables.delete_if {|k,io|
+ if io.close_scheduled?
+ io.close
+ begin
+ EventMachine::event_callback io.uuid, ConnectionUnbound, nil
+ rescue ConnectionNotBound; end
+ true
+ end
+ }
+ end
+
+ # #stop
+ def stop
+ raise Error.new( "not running") unless @running
+ @stop_scheduled = true
+ end
+
+ def open_loopbreaker
+ # Can't use an IO.pipe because they can't be set nonselectable in Windows.
+ # Pick a random localhost UDP port.
+ #@loopbreak_writer.close if @loopbreak_writer
+ #rd,@loopbreak_writer = IO.pipe
+ @loopbreak_reader = UDPSocket.new
+ @loopbreak_writer = UDPSocket.new
+ bound = false
+ 100.times {
+ @loopbreak_port = rand(10000) + 40000
+ begin
+ @loopbreak_reader.bind "127.0.0.1", @loopbreak_port
+ bound = true
+ break
+ rescue
+ end
+ }
+ raise "Unable to bind Loopbreaker" unless bound
+ LoopbreakReader.new(@loopbreak_reader)
+ end
+
+ def close_loopbreaker
+ @loopbreak_writer.close
+ @loopbreak_writer = nil
+ end
+
+ def signal_loopbreak
+ begin
+ @loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer
+ rescue IOError; end
+ end
+
+ def set_timer_quantum interval_in_seconds
+ @timer_quantum = interval_in_seconds
+ end
+
+ end
+
+end
+
+# @private
+class IO
+ extend Forwardable
+ def_delegator :@my_selectable, :close_scheduled?
+ def_delegator :@my_selectable, :select_for_reading?
+ def_delegator :@my_selectable, :select_for_writing?
+ def_delegator :@my_selectable, :eventable_read
+ def_delegator :@my_selectable, :eventable_write
+ def_delegator :@my_selectable, :uuid
+ def_delegator :@my_selectable, :is_server
+ def_delegator :@my_selectable, :is_server=
+ def_delegator :@my_selectable, :send_data
+ def_delegator :@my_selectable, :schedule_close
+ def_delegator :@my_selectable, :get_peername
+ def_delegator :@my_selectable, :get_sockname
+ def_delegator :@my_selectable, :send_datagram
+ def_delegator :@my_selectable, :get_outbound_data_size
+ def_delegator :@my_selectable, :set_inactivity_timeout
+ def_delegator :@my_selectable, :heartbeat
+ def_delegator :@my_selectable, :io
+ def_delegator :@my_selectable, :io=
+end
+
+module EventMachine
+ # @private
+ class Selectable
+
+ attr_accessor :io, :is_server
+ attr_reader :uuid
+
+ def initialize io
+ @io = io
+ @uuid = UuidGenerator.generate
+ @is_server = false
+ @last_activity = Reactor.instance.current_loop_time
+
+ if defined?(Fcntl::F_GETFL)
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
+ @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
+ else
+ # Windows doesn't define F_GETFL.
+ # It's not very reliable about setting descriptors nonblocking either.
+ begin
+ s = Socket.for_fd(@io.fileno)
+ s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )
+ rescue Errno::EINVAL, Errno::EBADF
+ warn "Serious error: unable to set descriptor non-blocking"
+ end
+ end
+ # TODO, should set CLOEXEC on Unix?
+
+ @close_scheduled = false
+ @close_requested = false
+
+ se = self; @io.instance_eval { @my_selectable = se }
+ Reactor.instance.add_selectable @io
+ end
+
+ def close_scheduled?
+ @close_scheduled
+ end
+
+ def select_for_reading?
+ false
+ end
+
+ def select_for_writing?
+ false
+ end
+
+ def get_peername
+ nil
+ end
+
+ def get_sockname
+ nil
+ end
+
+ def set_inactivity_timeout tm
+ @inactivity_timeout = tm
+ end
+
+ def heartbeat
+ end
+
+ def schedule_close(after_writing=false)
+ if after_writing
+ @close_requested = true
+ else
+ @close_scheduled = true
+ end
+ end
+ end
+
+end
+
+module EventMachine
+ # @private
+ class StreamObject < Selectable
+ def initialize io
+ super io
+ @outbound_q = []
+ end
+
+ # If we have to close, or a close-after-writing has been requested,
+ # then don't read any more data.
+ def select_for_reading?
+ true unless (@close_scheduled || @close_requested)
+ end
+
+ # If we have to close, don't select for writing.
+ # Otherwise, see if the protocol is ready to close.
+ # If not, see if he has data to send.
+ # If a close-after-writing has been requested and the outbound queue
+ # is empty, convert the status to close_scheduled.
+ def select_for_writing?
+ unless @close_scheduled
+ if @outbound_q.empty?
+ @close_scheduled = true if @close_requested
+ false
+ else
+ true
+ end
+ end
+ end
+
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
+ # If we have it, then we can read multiple times safely to improve
+ # performance.
+ # The last-activity clock ASSUMES that we only come here when we
+ # have selected readable.
+ # TODO, coalesce multiple reads into a single event.
+ # TODO, do the function check somewhere else and cache it.
+ def eventable_read
+ @last_activity = Reactor.instance.current_loop_time
+ begin
+ if io.respond_to?(:read_nonblock)
+ 10.times {
+ data = io.read_nonblock(4096)
+ EventMachine::event_callback uuid, ConnectionData, data
+ }
+ else
+ data = io.sysread(4096)
+ EventMachine::event_callback uuid, ConnectionData, data
+ end
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, SSLConnectionWaitReadable
+ # no-op
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Errno::EPIPE, OpenSSL::SSL::SSLError
+ @close_scheduled = true
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
+ end
+
+ end
+
+ # Provisional implementation. Will be re-implemented in subclasses.
+ # TODO: Complete this implementation. As it stands, this only writes
+ # a single packet per cycle. Highly inefficient, but required unless
+ # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
+ # built from sources from May 25, 2006 or newer).
+ # We need to improve the loop so it writes multiple times, however
+ # not more than a certain number of bytes per cycle, otherwise
+ # one busy connection could hog output buffers and slow down other
+ # connections. Also we should coalesce small writes.
+ # URGENT TODO: Coalesce small writes. They are a performance killer.
+ # The last-activity recorder ASSUMES we'll only come here if we've
+ # selected writable.
+ def eventable_write
+ # coalesce the outbound array here, perhaps
+ @last_activity = Reactor.instance.current_loop_time
+ while data = @outbound_q.shift do
+ begin
+ data = data.to_s
+ w = if io.respond_to?(:write_nonblock)
+ io.write_nonblock data
+ else
+ io.syswrite data
+ end
+
+ if w < data.length
+ @outbound_q.unshift data[w..-1]
+ break
+ end
+ rescue Errno::EAGAIN, SSLConnectionWaitReadable, SSLConnectionWaitWritable
+ @outbound_q.unshift data
+ break
+ rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE, OpenSSL::SSL::SSLError
+ @close_scheduled = true
+ @outbound_q.clear
+ end
+ end
+
+ end
+
+ # #send_data
+ def send_data data
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
+ unless @close_scheduled or @close_requested or !data or data.length <= 0
+ @outbound_q << data.to_s
+ end
+ end
+
+ # #get_peername
+ # This is defined in the normal way on connected stream objects.
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
+ # We could also use a convenience method that did the unpacking automatically.
+ def get_peername
+ io.getpeername
+ end
+
+ # #get_sockname
+ # This is defined in the normal way on connected stream objects.
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
+ # We could also use a convenience method that did the unpacking automatically.
+ def get_sockname
+ io.getsockname
+ end
+
+ # #get_outbound_data_size
+ def get_outbound_data_size
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
+ end
+
+ def heartbeat
+ if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
+ schedule_close true
+ end
+ end
+ end
+
+
+end
+
+
+#--------------------------------------------------------------
+
+
+
+module EventMachine
+ # @private
+ class EvmaTCPClient < StreamObject
+
+ def self.connect bind_addr, bind_port, host, port
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
+ sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr )) if bind_addr
+
+ begin
+ # TODO, this assumes a current Ruby snapshot.
+ # We need to degrade to a nonblocking connect otherwise.
+ sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
+ rescue Errno::ECONNREFUSED, Errno::EINPROGRESS
+ end
+ EvmaTCPClient.new sd
+ end
+
+ def initialize io
+ super
+ @pending = true
+ @handshake_complete = false
+ end
+
+ def ready?
+ if RUBY_PLATFORM =~ /linux/
+ io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).unpack("i").first == 1 # TCP_ESTABLISHED
+ else
+ io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first == 0 # NO ERROR
+ end
+ end
+
+ def handshake_complete?
+ if !@handshake_complete && io.respond_to?(:state)
+ if io.state =~ /^SSLOK/
+ @handshake_complete = true
+ EventMachine::event_callback uuid, SslHandshakeCompleted, ""
+ EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert
+ end
+ else
+ @handshake_complete = true
+ end
+ @handshake_complete
+ end
+
+ def pending?
+ handshake_complete?
+ if @pending
+ if ready?
+ @pending = false
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
+ end
+ end
+ @pending
+ end
+
+ def select_for_writing?
+ pending?
+ super
+ end
+
+ def select_for_reading?
+ pending?
+ super
+ end
+ end
+end
+
+
+
+module EventMachine
+ # @private
+ class EvmaKeyboard < StreamObject
+
+ def self.open
+ EvmaKeyboard.new STDIN
+ end
+
+
+ def initialize io
+ super
+ end
+
+
+ def select_for_writing?
+ false
+ end
+
+ def select_for_reading?
+ true
+ end
+
+
+ end
+end
+
+
+
+module EventMachine
+ # @private
+ class EvmaUNIXClient < StreamObject
+
+ def self.connect chain
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
+ begin
+ # TODO, this assumes a current Ruby snapshot.
+ # We need to degrade to a nonblocking connect otherwise.
+ sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
+ rescue Errno::EINPROGRESS
+ end
+ EvmaUNIXClient.new sd
+ end
+
+
+ def initialize io
+ super
+ @pending = true
+ end
+
+
+ def select_for_writing?
+ @pending ? true : super
+ end
+
+ def select_for_reading?
+ @pending ? false : super
+ end
+
+ def eventable_write
+ if @pending
+ @pending = false
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
+ end
+ else
+ super
+ end
+ end
+
+
+
+ end
+end
+
+
+#--------------------------------------------------------------
+
+module EventMachine
+ # @private
+ class EvmaTCPServer < Selectable
+
+ # TODO, refactor and unify with EvmaUNIXServer.
+
+ class << self
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
+ # with an object of type TCPServer. Prior versions won't so we
+ # play it safe and just build a socket.
+ #
+ def start_server host, port
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
+ sd.bind( Socket.pack_sockaddr_in( port, host ))
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
+ EvmaTCPServer.new sd
+ end
+ end
+
+ def initialize io
+ super io
+ end
+
+
+ def select_for_reading?
+ true
+ end
+
+ #--
+ # accept_nonblock returns an array consisting of the accepted
+ # socket and a sockaddr_in which names the peer.
+ # Don't accept more than 10 at a time.
+ def eventable_read
+ begin
+ 10.times {
+ descriptor,peername = io.accept_nonblock
+ sd = EvmaTCPClient.new descriptor
+ sd.is_server = true
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
+ }
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
+ end
+ end
+
+ #--
+ #
+ def schedule_close
+ @close_scheduled = true
+ end
+
+ end
+end
+
+
+#--------------------------------------------------------------
+
+module EventMachine
+ # @private
+ class EvmaUNIXServer < Selectable
+
+ # TODO, refactor and unify with EvmaTCPServer.
+
+ class << self
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
+ # with an object of type TCPServer. Prior versions won't so we
+ # play it safe and just build a socket.
+ #
+ def start_server chain
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
+ sd.bind( Socket.pack_sockaddr_un( chain ))
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
+ EvmaUNIXServer.new sd
+ end
+ end
+
+ def initialize io
+ super io
+ end
+
+
+ def select_for_reading?
+ true
+ end
+
+ #--
+ # accept_nonblock returns an array consisting of the accepted
+ # socket and a sockaddr_in which names the peer.
+ # Don't accept more than 10 at a time.
+ def eventable_read
+ begin
+ 10.times {
+ descriptor,peername = io.accept_nonblock
+ sd = StreamObject.new descriptor
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
+ }
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
+ end
+ end
+
+ #--
+ #
+ def schedule_close
+ @close_scheduled = true
+ end
+
+ end
+end
+
+
+
+#--------------------------------------------------------------
+
+module EventMachine
+ # @private
+ class LoopbreakReader < Selectable
+
+ def select_for_reading?
+ true
+ end
+
+ def eventable_read
+ io.sysread(128)
+ EventMachine::event_callback "", LoopbreakSignalled, ""
+ end
+
+ end
+end
+
+
+
+# @private
+module EventMachine
+ # @private
+ class DatagramObject < Selectable
+ def initialize io
+ super io
+ @outbound_q = []
+ end
+
+ # #send_datagram
+ def send_datagram data, target
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
+ unless @close_scheduled or @close_requested
+ @outbound_q << [data.to_s, target]
+ end
+ end
+
+ # #select_for_writing?
+ def select_for_writing?
+ unless @close_scheduled
+ if @outbound_q.empty?
+ @close_scheduled = true if @close_requested
+ false
+ else
+ true
+ end
+ end
+ end
+
+ # #select_for_reading?
+ def select_for_reading?
+ true
+ end
+
+ # #get_outbound_data_size
+ def get_outbound_data_size
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
+ end
+
+
+ end
+
+
+end
+
+
+module EventMachine
+ # @private
+ class EvmaUDPSocket < DatagramObject
+
+ class << self
+ def create host, port
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
+ sd.bind Socket::pack_sockaddr_in( port, host )
+ EvmaUDPSocket.new sd
+ end
+ end
+
+ # #eventable_write
+ # This really belongs in DatagramObject, but there is some UDP-specific stuff.
+ def eventable_write
+ 40.times {
+ break if @outbound_q.empty?
+ begin
+ data,target = @outbound_q.first
+
+ # This damn better be nonblocking.
+ io.send data.to_s, 0, target
+
+ @outbound_q.shift
+ rescue Errno::EAGAIN
+ # It's not been observed in testing that we ever get here.
+ # True to the definition, packets will be accepted and quietly dropped
+ # if the system is under pressure.
+ break
+ rescue EOFError, Errno::ECONNRESET
+ @close_scheduled = true
+ @outbound_q.clear
+ end
+ }
+ end
+
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
+ # If we have it, then we can read multiple times safely to improve
+ # performance.
+ def eventable_read
+ begin
+ if io.respond_to?(:recvfrom_nonblock)
+ 40.times {
+ data,@return_address = io.recvfrom_nonblock(16384)
+ EventMachine::event_callback uuid, ConnectionData, data
+ @return_address = nil
+ }
+ else
+ raise "unimplemented datagram-read operation on this Ruby"
+ end
+ rescue Errno::EAGAIN
+ # no-op
+ rescue Errno::ECONNRESET, EOFError
+ @close_scheduled = true
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
+ end
+ end
+
+ def send_data data
+ send_datagram data, @return_address
+ end
+ end
+end
+
+# load base EM api on top, now that we have the underlying pure ruby
+# implementation defined
+require 'eventmachine'
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/queue.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/queue.rb
new file mode 100644
index 0000000..9096ed0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/queue.rb
@@ -0,0 +1,80 @@
+module EventMachine
+ # A cross thread, reactor scheduled, linear queue.
+ #
+ # This class provides a simple queue abstraction on top of the reactor
+ # scheduler. It services two primary purposes:
+ #
+ # * API sugar for stateful protocols
+ # * Pushing processing onto the reactor thread
+ #
+ # @example
+ #
+ # q = EM::Queue.new
+ # q.push('one', 'two', 'three')
+ # 3.times do
+ # q.pop { |msg| puts(msg) }
+ # end
+ #
+ class Queue
+ def initialize
+ @sink = []
+ @drain = []
+ @popq = []
+ end
+
+ # Pop items off the queue, running the block on the reactor thread. The pop
+ # will not happen immediately, but at some point in the future, either in
+ # the next tick, if the queue has data, or when the queue is populated.
+ #
+ # @return [NilClass] nil
+ def pop(*a, &b)
+ cb = EM::Callback(*a, &b)
+ EM.schedule do
+ if @drain.empty?
+ @drain = @sink
+ @sink = []
+ end
+ if @drain.empty?
+ @popq << cb
+ else
+ cb.call @drain.shift
+ end
+ end
+ nil # Always returns nil
+ end
+
+ # Push items onto the queue in the reactor thread. The items will not appear
+ # in the queue immediately, but will be scheduled for addition during the
+ # next reactor tick.
+ def push(*items)
+ EM.schedule do
+ @sink.push(*items)
+ unless @popq.empty?
+ @drain = @sink
+ @sink = []
+ @popq.shift.call @drain.shift until @drain.empty? || @popq.empty?
+ end
+ end
+ end
+ alias :<< :push
+
+ # @return [Boolean]
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
+ def empty?
+ @drain.empty? && @sink.empty?
+ end
+
+ # @return [Integer] Queue size
+ # @note This is a peek, it's not thread safe, and may only tend toward accuracy.
+ def size
+ @drain.size + @sink.size
+ end
+
+ # @return [Integer] Waiting size
+ # @note This is a peek at the number of jobs that are currently waiting on the Queue
+ def num_waiting
+ @popq.size
+ end
+
+ end # Queue
+end # EventMachine
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb
new file mode 100644
index 0000000..1d2d7aa
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/resolver.rb
@@ -0,0 +1,232 @@
+module EventMachine
+ module DNS
+ class Resolver
+
+ def self.windows?
+ if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
+ require 'win32/resolv'
+ true
+ else
+ false
+ end
+ end
+
+ HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts'
+
+ @hosts = nil
+ @nameservers = nil
+ @socket = nil
+
+ def self.resolve(hostname)
+ Request.new(socket, hostname)
+ end
+
+ def self.socket
+ if @socket && @socket.error?
+ @socket = Socket.open
+ else
+ @socket ||= Socket.open
+ end
+ end
+
+ def self.nameservers=(ns)
+ @nameservers = ns
+ end
+
+ def self.nameservers
+ return @nameservers if @nameservers
+
+ if windows?
+ _, ns = Win32::Resolv.get_resolv_info
+ return @nameservers = ns || []
+ end
+
+ @nameservers = []
+ IO.readlines('/etc/resolv.conf').each do |line|
+ if line =~ /^nameserver (.+)$/
+ @nameservers << $1.split(/\s+/).first
+ end
+ end
+
+ @nameservers
+ rescue
+ @nameservers = []
+ end
+
+ def self.nameserver
+ nameservers.shuffle.first
+ end
+
+ def self.hosts
+ return @hosts if @hosts
+
+ @hosts = {}
+ IO.readlines(HOSTS_FILE).each do |line|
+ next if line =~ /^#/
+ addr, host = line.split(/\s+/)
+
+ next unless addr && host
+ @hosts[host] ||= []
+ @hosts[host] << addr
+ end
+
+ @hosts
+ rescue
+ @hosts = {}
+ end
+ end
+
+ class RequestIdAlreadyUsed < RuntimeError; end
+
+ class Socket < EventMachine::Connection
+ def self.open
+ EventMachine::open_datagram_socket('0.0.0.0', 0, self)
+ end
+
+ def initialize
+ @nameserver = nil
+ end
+
+ def post_init
+ @requests = {}
+ end
+
+ def start_timer
+ @timer ||= EM.add_periodic_timer(0.1, &method(:tick))
+ end
+
+ def stop_timer
+ EM.cancel_timer(@timer)
+ @timer = nil
+ end
+
+ def unbind
+ end
+
+ def tick
+ @requests.each do |id,req|
+ req.tick
+ end
+ end
+
+ def register_request(id, req)
+ if @requests.has_key?(id)
+ raise RequestIdAlreadyUsed
+ else
+ @requests[id] = req
+ end
+
+ start_timer
+ end
+
+ def deregister_request(id, req)
+ @requests.delete(id)
+ stop_timer if @requests.length == 0
+ end
+
+ def send_packet(pkt)
+ send_datagram(pkt, nameserver, 53)
+ end
+
+ def nameserver=(ns)
+ @nameserver = ns
+ end
+
+ def nameserver
+ @nameserver || Resolver.nameserver
+ end
+
+ # Decodes the packet, looks for the request and passes the
+ # response over to the requester
+ def receive_data(data)
+ msg = nil
+ begin
+ msg = Resolv::DNS::Message.decode data
+ rescue
+ else
+ req = @requests[msg.id]
+ if req
+ @requests.delete(msg.id)
+ stop_timer if @requests.length == 0
+ req.receive_answer(msg)
+ end
+ end
+ end
+ end
+
+ class Request
+ include Deferrable
+ attr_accessor :retry_interval, :max_tries
+
+ def initialize(socket, hostname)
+ @socket = socket
+ @hostname = hostname
+ @tries = 0
+ @last_send = Time.at(0)
+ @retry_interval = 3
+ @max_tries = 5
+
+ if addrs = Resolver.hosts[hostname]
+ succeed addrs
+ else
+ EM.next_tick { tick }
+ end
+ end
+
+ def tick
+ # Break early if nothing to do
+ return if @last_send + @retry_interval > Time.now
+ if @tries < @max_tries
+ send
+ else
+ @socket.deregister_request(@id, self)
+ fail 'retries exceeded'
+ end
+ end
+
+ def receive_answer(msg)
+ addrs = []
+ msg.each_answer do |name,ttl,data|
+ if data.kind_of?(Resolv::DNS::Resource::IN::A) ||
+ data.kind_of?(Resolv::DNS::Resource::IN::AAAA)
+ addrs << data.address.to_s
+ end
+ end
+
+ if addrs.empty?
+ fail "rcode=#{msg.rcode}"
+ else
+ succeed addrs
+ end
+ end
+
+ private
+
+ def send
+ @tries += 1
+ @last_send = Time.now
+ @socket.send_packet(packet.encode)
+ end
+
+ def id
+ begin
+ @id = rand(65535)
+ @socket.register_request(@id, self)
+ rescue RequestIdAlreadyUsed
+ retry
+ end unless defined?(@id)
+
+ @id
+ end
+
+ def packet
+ msg = Resolv::DNS::Message.new
+ msg.id = id
+ msg.rd = 1
+ msg.add_question @hostname, Resolv::DNS::Resource::IN::A
+ msg
+ end
+
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/spawnable.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/spawnable.rb
new file mode 100644
index 0000000..35c087d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/spawnable.rb
@@ -0,0 +1,84 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 25 Aug 2007
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+module EventMachine
+ # Support for Erlang-style processes.
+ #
+ class SpawnedProcess
+ # Send a message to the spawned process
+ def notify *x
+ me = self
+ EM.next_tick {
+ # A notification executes in the context of this
+ # SpawnedProcess object. That makes self and notify
+ # work as one would expect.
+ #
+ y = me.call(*x)
+ if y and y.respond_to?(:pull_out_yield_block)
+ a,b = y.pull_out_yield_block
+ set_receiver a
+ self.notify if b
+ end
+ }
+ end
+ alias_method :resume, :notify
+ alias_method :run, :notify # for formulations like (EM.spawn {xxx}).run
+
+ def set_receiver blk
+ (class << self ; self ; end).class_eval do
+ remove_method :call if method_defined? :call
+ define_method :call, blk
+ end
+ end
+
+ end
+
+ # @private
+ class YieldBlockFromSpawnedProcess
+ def initialize block, notify
+ @block = [block,notify]
+ end
+ def pull_out_yield_block
+ @block
+ end
+ end
+
+ # Spawn an erlang-style process
+ def self.spawn &block
+ s = SpawnedProcess.new
+ s.set_receiver block
+ s
+ end
+
+ # @private
+ def self.yield &block
+ return YieldBlockFromSpawnedProcess.new( block, false )
+ end
+
+ # @private
+ def self.yield_and_notify &block
+ return YieldBlockFromSpawnedProcess.new( block, true )
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/streamer.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/streamer.rb
new file mode 100644
index 0000000..cf49c88
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/streamer.rb
@@ -0,0 +1,118 @@
+module EventMachine
+ # Streams a file over a given connection. Streaming begins once the object is
+ # instantiated. Typically FileStreamer instances are not reused.
+ #
+ # Streaming uses buffering for files larger than 16K and uses so-called fast file reader (a C++ extension)
+ # if available (it is part of eventmachine gem itself).
+ #
+ # @example
+ #
+ # module FileSender
+ # def post_init
+ # streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar')
+ # streamer.callback{
+ # # file was sent successfully
+ # close_connection_after_writing
+ # }
+ # end
+ # end
+ #
+ #
+ # @author Francis Cianfrocca
+ class FileStreamer
+ include Deferrable
+
+ # Use mapped streamer for files bigger than 16k
+ MappingThreshold = 16384
+ # Wait until next tick to send more data when 50k is still in the outgoing buffer
+ BackpressureLevel = 50000
+ # Send 16k chunks at a time
+ ChunkSize = 16384
+
+ # @param [EventMachine::Connection] connection
+ # @param [String] filename File path
+ #
+ # @option args [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics.
+ def initialize connection, filename, args = {}
+ @connection = connection
+ @http_chunks = args[:http_chunks]
+
+ if File.exist?(filename)
+ @size = File.size(filename)
+ if @size <= MappingThreshold
+ stream_without_mapping filename
+ else
+ stream_with_mapping filename
+ end
+ else
+ fail "file not found"
+ end
+ end
+
+ # @private
+ def stream_without_mapping filename
+ if @http_chunks
+ @connection.send_data "#{@size.to_s(16)}\r\n"
+ @connection.send_file_data filename
+ @connection.send_data "\r\n0\r\n\r\n"
+ else
+ @connection.send_file_data filename
+ end
+ succeed
+ end
+ private :stream_without_mapping
+
+ # @private
+ def stream_with_mapping filename
+ ensure_mapping_extension_is_present
+
+ @position = 0
+ @mapping = EventMachine::FastFileReader::Mapper.new filename
+ stream_one_chunk
+ end
+ private :stream_with_mapping
+
+ # Used internally to stream one chunk at a time over multiple reactor ticks
+ # @private
+ def stream_one_chunk
+ loop {
+ if @position < @size
+ if @connection.get_outbound_data_size > BackpressureLevel
+ EventMachine::next_tick {stream_one_chunk}
+ break
+ else
+ len = @size - @position
+ len = ChunkSize if (len > ChunkSize)
+
+ @connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks
+ @connection.send_data( @mapping.get_chunk( @position, len ))
+ @connection.send_data("\r\n") if @http_chunks
+
+ @position += len
+ end
+ else
+ @connection.send_data "0\r\n\r\n" if @http_chunks
+ @mapping.close
+ succeed
+ break
+ end
+ }
+ end
+
+ #
+ # We use an outboard extension class to get memory-mapped files.
+ # It's outboard to avoid polluting the core distro, but that means
+ # there's a "hidden" dependency on it. The first time we get here in
+ # any run, try to load up the dependency extension. User code will see
+ # a LoadError if it's not available, but code that doesn't require
+ # mapped files will work fine without it. This is a somewhat difficult
+ # compromise between usability and proper modularization.
+ #
+ # @private
+ def ensure_mapping_extension_is_present
+ @@fastfilereader ||= (require 'fastfilereaderext')
+ end
+ private :ensure_mapping_extension_is_present
+
+ end # FileStreamer
+end # EventMachine
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/threaded_resource.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/threaded_resource.rb
new file mode 100644
index 0000000..87704d5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/threaded_resource.rb
@@ -0,0 +1,90 @@
+module EventMachine
+ # = EventMachine::ThreadedResource
+ #
+ # A threaded resource is a "quick and dirty" wrapper around the concept of
+ # wiring up synchronous code into a standard EM::Pool. This is useful to keep
+ # interfaces coherent and provide a simple approach at "making an interface
+ # async-ish".
+ #
+ # General usage is to wrap libraries that do not support EventMachine, or to
+ # have a specific number of dedicated high-cpu worker resources.
+ #
+ # == Basic Usage example
+ #
+ # This example requires the cassandra gem. The cassandra gem contains an
+ # EventMachine interface, but it's sadly Fiber based and thus only works on
+ # 1.9. It also requires (potentially) complex stack switching logic to reach
+ # completion of nested operations. By contrast this approach provides a block
+ # in which normal synchronous code can occur, but makes no attempt to wire the
+ # IO into EventMachines C++ IO implementations, instead relying on the reactor
+ # pattern in rb_thread_select.
+ #
+ # cassandra_dispatcher = ThreadedResource.new do
+ # Cassandra.new('allthethings', '127.0.0.1:9160')
+ # end
+ #
+ # pool = EM::Pool.new
+ #
+ # pool.add cassandra_dispatcher
+ #
+ # # If we don't care about the result:
+ # pool.perform do |dispatcher|
+ # # The following block executes inside a dedicated thread, and should not
+ # # access EventMachine things:
+ # dispatcher.dispatch do |cassandra|
+ # cassandra.insert(:Things, '10', 'stuff' => 'things')
+ # end
+ # end
+ #
+ # # Example where we care about the result:
+ # pool.perform do |dispatcher|
+ # # The dispatch block is executed in the resources thread.
+ # completion = dispatcher.dispatch do |cassandra|
+ # cassandra.get(:Things, '10', 'stuff')
+ # end
+ #
+ # # This block will be yielded on the EM thread:
+ # completion.callback do |result|
+ # EM.do_something_with(result)
+ # end
+ #
+ # completion
+ # end
+ class ThreadedResource
+
+ # The block should return the resource that will be yielded in a dispatch.
+ def initialize
+ @resource = yield
+
+ @running = true
+ @queue = ::Queue.new
+ @thread = Thread.new do
+ @queue.pop.call while @running
+ end
+ end
+
+ # Called on the EM thread, generally in a perform block to return a
+ # completion for the work.
+ def dispatch
+ completion = EM::Completion.new
+ @queue << lambda do
+ begin
+ result = yield @resource
+ completion.succeed result
+ rescue => e
+ completion.fail e
+ end
+ end
+ completion
+ end
+
+ # Kill the internal thread. should only be used to cleanup - generally
+ # only required for tests.
+ def shutdown
+ @running = false
+ @queue << lambda {}
+ @thread.join
+ end
+
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/tick_loop.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/tick_loop.rb
new file mode 100644
index 0000000..a95d516
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/tick_loop.rb
@@ -0,0 +1,85 @@
+module EventMachine
+ # Creates and immediately starts an EventMachine::TickLoop
+ def self.tick_loop(*a, &b)
+ TickLoop.new(*a, &b).start
+ end
+
+ # A TickLoop is useful when one needs to distribute amounts of work
+ # throughout ticks in order to maintain response times. It is also useful for
+ # simple repeated checks and metrics.
+ # @example
+ # # Here we run through an array one item per tick until it is empty,
+ # # printing each element.
+ # # When the array is empty, we return :stop from the callback, and the
+ # # loop will terminate.
+ # # When the loop terminates, the on_stop callbacks will be called.
+ # EM.run do
+ # array = (1..100).to_a
+ #
+ # tickloop = EM.tick_loop do
+ # if array.empty?
+ # :stop
+ # else
+ # puts array.shift
+ # end
+ # end
+ #
+ # tickloop.on_stop { EM.stop }
+ # end
+ #
+ class TickLoop
+
+ # Arguments: A callback (EM::Callback) to call each tick. If the call
+ # returns +:stop+ then the loop will be stopped. Any other value is
+ # ignored.
+ def initialize(*a, &b)
+ @work = EM::Callback(*a, &b)
+ @stops = []
+ @stopped = true
+ end
+
+ # Arguments: A callback (EM::Callback) to call once on the next stop (or
+ # immediately if already stopped).
+ def on_stop(*a, &b)
+ if @stopped
+ EM::Callback(*a, &b).call
+ else
+ @stops << EM::Callback(*a, &b)
+ end
+ end
+
+ # Stop the tick loop immediately, and call it's on_stop callbacks.
+ def stop
+ @stopped = true
+ until @stops.empty?
+ @stops.shift.call
+ end
+ end
+
+ # Query if the loop is stopped.
+ def stopped?
+ @stopped
+ end
+
+ # Start the tick loop, will raise argument error if the loop is already
+ # running.
+ def start
+ raise ArgumentError, "double start" unless @stopped
+ @stopped = false
+ schedule
+ end
+
+ private
+ def schedule
+ EM.next_tick do
+ next if @stopped
+ if @work.call == :stop
+ stop
+ else
+ schedule
+ end
+ end
+ self
+ end
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/timers.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/timers.rb
new file mode 100644
index 0000000..41cd959
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/timers.rb
@@ -0,0 +1,61 @@
+module EventMachine
+ # Creates a one-time timer
+ #
+ # timer = EventMachine::Timer.new(5) do
+ # # this will never fire because we cancel it
+ # end
+ # timer.cancel
+ #
+ class Timer
+ # Create a new timer that fires after a given number of seconds
+ def initialize interval, callback=nil, &block
+ @signature = EventMachine::add_timer(interval, callback || block)
+ end
+
+ # Cancel the timer
+ def cancel
+ EventMachine.send :cancel_timer, @signature
+ end
+ end
+
+ # Creates a periodic timer
+ #
+ # @example
+ # n = 0
+ # timer = EventMachine::PeriodicTimer.new(5) do
+ # puts "the time is #{Time.now}"
+ # timer.cancel if (n+=1) > 5
+ # end
+ #
+ class PeriodicTimer
+ # Create a new periodic timer that executes every interval seconds
+ def initialize interval, callback=nil, &block
+ @interval = interval
+ @code = callback || block
+ @cancelled = false
+ @work = method(:fire)
+ schedule
+ end
+
+ # Cancel the periodic timer
+ def cancel
+ @cancelled = true
+ end
+
+ # Fire the timer every interval seconds
+ attr_accessor :interval
+
+ # @private
+ def schedule
+ EventMachine::add_timer @interval, @work
+ end
+
+ # @private
+ def fire
+ unless @cancelled
+ @code.call
+ schedule
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/version.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/version.rb
new file mode 100644
index 0000000..ffc4310
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/em/version.rb
@@ -0,0 +1,3 @@
+module EventMachine
+ VERSION = "1.2.7"
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb
new file mode 100644
index 0000000..0873747
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/eventmachine.rb
@@ -0,0 +1,1601 @@
+if defined?(EventMachine.library_type) and EventMachine.library_type == :pure_ruby
+ # assume 'em/pure_ruby' was loaded already
+elsif RUBY_PLATFORM =~ /java/
+ require 'java'
+ require 'jeventmachine'
+else
+ begin
+ require 'rubyeventmachine'
+ rescue LoadError
+ warn "Unable to load the EventMachine C extension; To use the pure-ruby reactor, require 'em/pure_ruby'"
+ raise
+ end
+end
+
+require 'em/version'
+require 'em/pool'
+require 'em/deferrable'
+require 'em/future'
+require 'em/streamer'
+require 'em/spawnable'
+require 'em/processes'
+require 'em/iterator'
+require 'em/buftok'
+require 'em/timers'
+require 'em/protocols'
+require 'em/connection'
+require 'em/callback'
+require 'em/queue'
+require 'em/channel'
+require 'em/file_watch'
+require 'em/process_watch'
+require 'em/tick_loop'
+require 'em/resolver'
+require 'em/completion'
+require 'em/threaded_resource'
+
+require 'shellwords'
+require 'thread'
+require 'resolv'
+
+# Top-level EventMachine namespace. If you are looking for EventMachine examples, see {file:docs/GettingStarted.md EventMachine tutorial}.
+#
+# ## Key methods ##
+# ### Starting and stopping the event loop ###
+#
+# * {EventMachine.run}
+# * {EventMachine.stop_event_loop}
+#
+# ### Implementing clients ###
+#
+# * {EventMachine.connect}
+#
+# ### Implementing servers ###
+#
+# * {EventMachine.start_server}
+#
+# ### Working with timers ###
+#
+# * {EventMachine.add_timer}
+# * {EventMachine.add_periodic_timer}
+# * {EventMachine.cancel_timer}
+#
+# ### Working with blocking tasks ###
+#
+# * {EventMachine.defer}
+# * {EventMachine.next_tick}
+#
+# ### Efficient proxying ###
+#
+# * {EventMachine.enable_proxy}
+# * {EventMachine.disable_proxy}
+module EventMachine
+ class << self
+ # Exposed to allow joining on the thread, when run in a multithreaded
+ # environment. Performing other actions on the thread has undefined
+ # semantics (read: a dangerous endevor).
+ #
+ # @return [Thread]
+ attr_reader :reactor_thread
+ end
+ @next_tick_mutex = Mutex.new
+ @reactor_running = false
+ @next_tick_queue = []
+ @tails = []
+ @threadpool = @threadqueue = @resultqueue = nil
+ @all_threads_spawned = false
+
+ # System errnos
+ # @private
+ ERRNOS = Errno::constants.grep(/^E/).inject(Hash.new(:unknown)) { |hash, name|
+ errno = Errno.__send__(:const_get, name)
+ hash[errno::Errno] = errno
+ hash
+ }
+
+ # Initializes and runs an event loop. This method only returns if code inside the block passed to this method
+ # calls {EventMachine.stop_event_loop}. The block is executed after initializing its internal event loop but *before* running the loop,
+ # therefore this block is the right place to call any code that needs event loop to run, for example, {EventMachine.start_server},
+ # {EventMachine.connect} or similar methods of libraries that use EventMachine under the hood
+ # (like `EventMachine::HttpRequest.new` or `AMQP.start`).
+ #
+ # Programs that are run for long periods of time (e.g. servers) usually start event loop by calling {EventMachine.run}, and let it
+ # run "forever". It's also possible to use {EventMachine.run} to make a single client-connection to a remote server,
+ # process the data flow from that single connection, and then call {EventMachine.stop_event_loop} to stop, in other words,
+ # to run event loop for a short period of time (necessary to complete some operation) and then shut it down.
+ #
+ # Once event loop is running, it is perfectly possible to start multiple servers and clients simultaneously: content-aware
+ # proxies like [Proxymachine](https://github.com/mojombo/proxymachine) do just that.
+ #
+ # ## Using EventMachine with Ruby on Rails and other Web application frameworks ##
+ #
+ # Standalone applications often run event loop on the main thread, thus blocking for their entire lifespan. In case of Web applications,
+ # if you are running an EventMachine-based app server such as [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/),
+ # they start event loop for you. Servers like Unicorn, Apache Passenger or Mongrel occupy main Ruby thread to serve HTTP(S) requests. This means
+ # that calling {EventMachine.run} on the same thread is not an option (it will result in Web server never binding to the socket).
+ # In that case, start event loop in a separate thread as demonstrated below.
+ #
+ #
+ # @example Starting EventMachine event loop in the current thread to run the "Hello, world"-like Echo server example
+ #
+ # #!/usr/bin/env ruby
+ #
+ # require 'rubygems' # or use Bundler.setup
+ # require 'eventmachine'
+ #
+ # class EchoServer < EM::Connection
+ # def receive_data(data)
+ # send_data(data)
+ # end
+ # end
+ #
+ # EventMachine.run do
+ # EventMachine.start_server("0.0.0.0", 10000, EchoServer)
+ # end
+ #
+ #
+ # @example Starting EventMachine event loop in a separate thread
+ #
+ # # doesn't block current thread, can be used with Ruby on Rails, Sinatra, Merb, Rack
+ # # and any other application server that occupies main Ruby thread.
+ # Thread.new { EventMachine.run }
+ #
+ #
+ # @note This method blocks calling thread. If you need to start EventMachine event loop from a Web app
+ # running on a non event-driven server (Unicorn, Apache Passenger, Mongrel), do it in a separate thread like demonstrated
+ # in one of the examples.
+ # @see file:docs/GettingStarted.md Getting started with EventMachine
+ # @see EventMachine.stop_event_loop
+ def self.run blk=nil, tail=nil, &block
+ # Obsoleted the use_threads mechanism.
+ # 25Nov06: Added the begin/ensure block. We need to be sure that release_machine
+ # gets called even if an exception gets thrown within any of the user code
+ # that the event loop runs. The best way to see this is to run a unit
+ # test with two functions, each of which calls {EventMachine.run} and each of
+ # which throws something inside of #run. Without the ensure, the second test
+ # will start without release_machine being called and will immediately throw
+
+ #
+ if @reactor_running and @reactor_pid != Process.pid
+ # Reactor was started in a different parent, meaning we have forked.
+ # Clean up reactor state so a new reactor boots up in this child.
+ stop_event_loop
+ release_machine
+ cleanup_machine
+ @reactor_running = false
+ end
+
+ tail and @tails.unshift(tail)
+
+ if reactor_running?
+ (b = blk || block) and b.call # next_tick(b)
+ else
+ @conns = {}
+ @acceptors = {}
+ @timers = {}
+ @wrapped_exception = nil
+ @next_tick_queue ||= []
+ @tails ||= []
+ begin
+ initialize_event_machine
+ @reactor_pid = Process.pid
+ @reactor_thread = Thread.current
+ @reactor_running = true
+
+ (b = blk || block) and add_timer(0, b)
+ if @next_tick_queue && !@next_tick_queue.empty?
+ add_timer(0) { signal_loopbreak }
+ end
+
+ # Rubinius needs to come back into "Ruby space" for GC to work,
+ # so we'll crank the machine here.
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
+ while run_machine_once; end
+ else
+ run_machine
+ end
+
+ ensure
+ until @tails.empty?
+ @tails.pop.call
+ end
+
+ release_machine
+ cleanup_machine
+ @reactor_running = false
+ @reactor_thread = nil
+ end
+
+ raise @wrapped_exception if @wrapped_exception
+ end
+ end
+
+ # Sugars a common use case. Will pass the given block to #run, but will terminate
+ # the reactor loop and exit the function as soon as the code in the block completes.
+ # (Normally, {EventMachine.run} keeps running indefinitely, even after the block supplied to it
+ # finishes running, until user code calls {EventMachine.stop})
+ #
+ def self.run_block &block
+ pr = proc {
+ block.call
+ EventMachine::stop
+ }
+ run(&pr)
+ end
+
+ # @return [Boolean] true if the calling thread is the same thread as the reactor.
+ def self.reactor_thread?
+ Thread.current == @reactor_thread
+ end
+
+ # Runs the given callback on the reactor thread, or immediately if called
+ # from the reactor thread. Accepts the same arguments as {EventMachine::Callback}
+ def self.schedule(*a, &b)
+ cb = Callback(*a, &b)
+ if reactor_running? && reactor_thread?
+ cb.call
+ else
+ next_tick { cb.call }
+ end
+ end
+
+ # Forks a new process, properly stops the reactor and then calls {EventMachine.run} inside of it again, passing your block.
+ def self.fork_reactor &block
+ # This implementation is subject to change, especially if we clean up the relationship
+ # of EM#run to @reactor_running.
+ # Original patch by Aman Gupta.
+ #
+ Kernel.fork do
+ if reactor_running?
+ stop_event_loop
+ release_machine
+ cleanup_machine
+ @reactor_running = false
+ @reactor_thread = nil
+ end
+ run block
+ end
+ end
+
+ # Clean up Ruby space following a release_machine
+ def self.cleanup_machine
+ if @threadpool && !@threadpool.empty?
+ # Tell the threads to stop
+ @threadpool.each { |t| t.exit }
+ # Join the threads or bump the stragglers one more time
+ @threadpool.each { |t| t.join 0.01 || t.exit }
+ end
+ @threadpool = nil
+ @threadqueue = nil
+ @resultqueue = nil
+ @all_threads_spawned = false
+ @next_tick_queue = []
+ end
+
+ # Adds a block to call as the reactor is shutting down.
+ #
+ # These callbacks are called in the _reverse_ order to which they are added.
+ #
+ # @example Scheduling operations to be run when EventMachine event loop is stopped
+ #
+ # EventMachine.run do
+ # EventMachine.add_shutdown_hook { puts "b" }
+ # EventMachine.add_shutdown_hook { puts "a" }
+ # EventMachine.stop
+ # end
+ #
+ # # Outputs:
+ # # a
+ # # b
+ #
+ def self.add_shutdown_hook &block
+ @tails << block
+ end
+
+ # Adds a one-shot timer to the event loop.
+ # Call it with one or two parameters. The first parameters is a delay-time
+ # expressed in *seconds* (not milliseconds). The second parameter, if
+ # present, must be an object that responds to :call. If 2nd parameter is not given, then you
+ # can also simply pass a block to the method call.
+ #
+ # This method may be called from the block passed to {EventMachine.run}
+ # or from any callback method. It schedules execution of the proc or block
+ # passed to it, after the passage of an interval of time equal to
+ # *at least* the number of seconds specified in the first parameter to
+ # the call.
+ #
+ # {EventMachine.add_timer} is a non-blocking method. Callbacks can and will
+ # be called during the interval of time that the timer is in effect.
+ # There is no built-in limit to the number of timers that can be outstanding at
+ # any given time.
+ #
+ # @example Setting a one-shot timer with EventMachine
+ #
+ # EventMachine.run {
+ # puts "Starting the run now: #{Time.now}"
+ # EventMachine.add_timer 5, proc { puts "Executing timer event: #{Time.now}" }
+ # EventMachine.add_timer(10) { puts "Executing timer event: #{Time.now}" }
+ # }
+ #
+ # @param [Integer] delay Delay in seconds
+ # @see EventMachine::Timer
+ # @see EventMachine.add_periodic_timer
+ def self.add_timer *args, &block
+ interval = args.shift
+ code = args.shift || block
+ if code
+ # check too many timers!
+ s = add_oneshot_timer((interval.to_f * 1000).to_i)
+ @timers[s] = code
+ s
+ end
+ end
+
+ # Adds a periodic timer to the event loop.
+ # It takes the same parameters as the one-shot timer method, {EventMachine.add_timer}.
+ # This method schedules execution of the given block repeatedly, at intervals
+ # of time *at least* as great as the number of seconds given in the first
+ # parameter to the call.
+ #
+ # @example Write a dollar-sign to stderr every five seconds, without blocking
+ #
+ # EventMachine.run {
+ # EventMachine.add_periodic_timer( 5 ) { $stderr.write "$" }
+ # }
+ #
+ # @param [Integer] delay Delay in seconds
+ #
+ # @see EventMachine::PeriodicTimer
+ # @see EventMachine.add_timer
+ #
+ def self.add_periodic_timer *args, &block
+ interval = args.shift
+ code = args.shift || block
+
+ EventMachine::PeriodicTimer.new(interval, code)
+ end
+
+
+ # Cancel a timer (can be a callback or an {EventMachine::Timer} instance).
+ #
+ # @param [#cancel, #call] timer_or_sig A timer to cancel
+ # @see EventMachine::Timer#cancel
+ def self.cancel_timer timer_or_sig
+ if timer_or_sig.respond_to? :cancel
+ timer_or_sig.cancel
+ else
+ @timers[timer_or_sig] = false if @timers.has_key?(timer_or_sig)
+ end
+ end
+
+
+ # Causes the processing loop to stop executing, which will cause all open connections and accepting servers
+ # to be run down and closed. Connection termination callbacks added using {EventMachine.add_shutdown_hook}
+ # will be called as part of running this method.
+ #
+ # When all of this processing is complete, the call to {EventMachine.run} which started the processing loop
+ # will return and program flow will resume from the statement following {EventMachine.run} call.
+ #
+ # @example Stopping a running EventMachine event loop
+ #
+ # require 'rubygems'
+ # require 'eventmachine'
+ #
+ # module Redmond
+ # def post_init
+ # puts "We're sending a dumb HTTP request to the remote peer."
+ # send_data "GET / HTTP/1.1\r\nHost: www.microsoft.com\r\n\r\n"
+ # end
+ #
+ # def receive_data data
+ # puts "We received #{data.length} bytes from the remote peer."
+ # puts "We're going to stop the event loop now."
+ # EventMachine::stop_event_loop
+ # end
+ #
+ # def unbind
+ # puts "A connection has terminated."
+ # end
+ # end
+ #
+ # puts "We're starting the event loop now."
+ # EventMachine.run {
+ # EventMachine.connect "www.microsoft.com", 80, Redmond
+ # }
+ # puts "The event loop has stopped."
+ #
+ # # This program will produce approximately the following output:
+ # #
+ # # We're starting the event loop now.
+ # # We're sending a dumb HTTP request to the remote peer.
+ # # We received 1440 bytes from the remote peer.
+ # # We're going to stop the event loop now.
+ # # A connection has terminated.
+ # # The event loop has stopped.
+ #
+ #
+ def self.stop_event_loop
+ EventMachine::stop
+ end
+
+ # Initiates a TCP server (socket acceptor) on the specified IP address and port.
+ #
+ # The IP address must be valid on the machine where the program
+ # runs, and the process must be privileged enough to listen
+ # on the specified port (on Unix-like systems, superuser privileges
+ # are usually required to listen on any port lower than 1024).
+ # Only one listener may be running on any given address/port
+ # combination. start_server will fail if the given address and port
+ # are already listening on the machine, either because of a prior call
+ # to {.start_server} or some unrelated process running on the machine.
+ # If {.start_server} succeeds, the new network listener becomes active
+ # immediately and starts accepting connections from remote peers,
+ # and these connections generate callback events that are processed
+ # by the code specified in the handler parameter to {.start_server}.
+ #
+ # The optional handler which is passed to this method is the key
+ # to EventMachine's ability to handle particular network protocols.
+ # The handler parameter passed to start_server must be a Ruby Module
+ # that you must define. When the network server that is started by
+ # start_server accepts a new connection, it instantiates a new
+ # object of an anonymous class that is inherited from {EventMachine::Connection},
+ # *into which your handler module have been included*. Arguments passed into start_server
+ # after the class name are passed into the constructor during the instantiation.
+ #
+ # Your handler module may override any of the methods in {EventMachine::Connection},
+ # such as {EventMachine::Connection#receive_data}, in order to implement the specific behavior
+ # of the network protocol.
+ #
+ # Callbacks invoked in response to network events *always* take place
+ # within the execution context of the object derived from {EventMachine::Connection}
+ # extended by your handler module. There is one object per connection, and
+ # all of the callbacks invoked for a particular connection take the form
+ # of instance methods called against the corresponding {EventMachine::Connection}
+ # object. Therefore, you are free to define whatever instance variables you
+ # wish, in order to contain the per-connection state required by the network protocol you are
+ # implementing.
+ #
+ # {EventMachine.start_server} is usually called inside the block passed to {EventMachine.run},
+ # but it can be called from any EventMachine callback. {EventMachine.start_server} will fail
+ # unless the EventMachine event loop is currently running (which is why
+ # it's often called in the block suppled to {EventMachine.run}).
+ #
+ # You may call start_server any number of times to start up network
+ # listeners on different address/port combinations. The servers will
+ # all run simultaneously. More interestingly, each individual call to start_server
+ # can specify a different handler module and thus implement a different
+ # network protocol from all the others.
+ #
+ # @example
+ #
+ # require 'rubygems'
+ # require 'eventmachine'
+ #
+ # # Here is an example of a server that counts lines of input from the remote
+ # # peer and sends back the total number of lines received, after each line.
+ # # Try the example with more than one client connection opened via telnet,
+ # # and you will see that the line count increments independently on each
+ # # of the client connections. Also very important to note, is that the
+ # # handler for the receive_data function, which our handler redefines, may
+ # # not assume that the data it receives observes any kind of message boundaries.
+ # # Also, to use this example, be sure to change the server and port parameters
+ # # to the start_server call to values appropriate for your environment.
+ # module LineCounter
+ # MaxLinesPerConnection = 10
+ #
+ # def post_init
+ # puts "Received a new connection"
+ # @data_received = ""
+ # @line_count = 0
+ # end
+ #
+ # def receive_data data
+ # @data_received << data
+ # while @data_received.slice!( /^[^\n]*[\n]/m )
+ # @line_count += 1
+ # send_data "received #{@line_count} lines so far\r\n"
+ # @line_count == MaxLinesPerConnection and close_connection_after_writing
+ # end
+ # end
+ # end
+ #
+ # EventMachine.run {
+ # host, port = "192.168.0.100", 8090
+ # EventMachine.start_server host, port, LineCounter
+ # puts "Now accepting connections on address #{host}, port #{port}..."
+ # EventMachine.add_periodic_timer(10) { $stderr.write "*" }
+ # }
+ #
+ # @param [String] server Host to bind to.
+ # @param [Integer] port Port to bind to.
+ # @param [Module, Class] handler A module or class that implements connection callbacks
+ #
+ # @note Don't forget that in order to bind to ports < 1024 on Linux, *BSD and Mac OS X your process must have superuser privileges.
+ #
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ # @see EventMachine.stop_server
+ def self.start_server server, port=nil, handler=nil, *args, &block
+ begin
+ port = Integer(port)
+ rescue ArgumentError, TypeError
+ # there was no port, so server must be a unix domain socket
+ # the port argument is actually the handler, and the handler is one of the args
+ args.unshift handler if handler
+ handler = port
+ port = nil
+ end if port
+
+ klass = klass_from_handler(Connection, handler, *args)
+
+ s = if port
+ start_tcp_server server, port
+ else
+ start_unix_server server
+ end
+ @acceptors[s] = [klass,args,block]
+ s
+ end
+
+ # Attach to an existing socket's file descriptor. The socket may have been
+ # started with {EventMachine.start_server}.
+ def self.attach_server sock, handler=nil, *args, &block
+ klass = klass_from_handler(Connection, handler, *args)
+ sd = sock.respond_to?(:fileno) ? sock.fileno : sock
+ s = attach_sd(sd)
+ @acceptors[s] = [klass,args,block,sock]
+ s
+ end
+
+ # Stop a TCP server socket that was started with {EventMachine.start_server}.
+ # @see EventMachine.start_server
+ def self.stop_server signature
+ EventMachine::stop_tcp_server signature
+ end
+
+ # Start a Unix-domain server.
+ #
+ # Note that this is an alias for {EventMachine.start_server}, which can be used to start both
+ # TCP and Unix-domain servers.
+ #
+ # @see EventMachine.start_server
+ def self.start_unix_domain_server filename, *args, &block
+ start_server filename, *args, &block
+ end
+
+ # Initiates a TCP connection to a remote server and sets up event handling for the connection.
+ # {EventMachine.connect} requires event loop to be running (see {EventMachine.run}).
+ #
+ # {EventMachine.connect} takes the IP address (or hostname) and
+ # port of the remote server you want to connect to.
+ # It also takes an optional handler (a module or a subclass of {EventMachine::Connection}) which you must define, that
+ # contains the callbacks that will be invoked by the event loop on behalf of the connection.
+ #
+ # Learn more about connection lifecycle callbacks in the {file:docs/GettingStarted.md EventMachine tutorial} and
+ # {file:docs/ConnectionLifecycleCallbacks.md Connection lifecycle guide}.
+ #
+ #
+ # @example
+ #
+ # # Here's a program which connects to a web server, sends a naive
+ # # request, parses the HTTP header of the response, and then
+ # # (antisocially) ends the event loop, which automatically drops the connection
+ # # (and incidentally calls the connection's unbind method).
+ # module DumbHttpClient
+ # def post_init
+ # send_data "GET / HTTP/1.1\r\nHost: _\r\n\r\n"
+ # @data = ""
+ # @parsed = false
+ # end
+ #
+ # def receive_data data
+ # @data << data
+ # if !@parsed and @data =~ /[\n][\r]*[\n]/m
+ # @parsed = true
+ # puts "RECEIVED HTTP HEADER:"
+ # $`.each {|line| puts ">>> #{line}" }
+ #
+ # puts "Now we'll terminate the loop, which will also close the connection"
+ # EventMachine::stop_event_loop
+ # end
+ # end
+ #
+ # def unbind
+ # puts "A connection has terminated"
+ # end
+ # end
+ #
+ # EventMachine.run {
+ # EventMachine.connect "www.bayshorenetworks.com", 80, DumbHttpClient
+ # }
+ # puts "The event loop has ended"
+ #
+ #
+ # @example Defining protocol handler as a class
+ #
+ # class MyProtocolHandler < EventMachine::Connection
+ # def initialize *args
+ # super
+ # # whatever else you want to do here
+ # end
+ #
+ # # ...
+ # end
+ #
+ #
+ # @param [String] server Host to connect to
+ # @param [Integer] port Port to connect to
+ # @param [Module, Class] handler A module or class that implements connection lifecycle callbacks
+ #
+ # @see EventMachine.start_server
+ # @see file:docs/GettingStarted.md EventMachine tutorial
+ def self.connect server, port=nil, handler=nil, *args, &blk
+ # EventMachine::connect initiates a TCP connection to a remote
+ # server and sets up event-handling for the connection.
+ # It internally creates an object that should not be handled
+ # by the caller. HOWEVER, it's often convenient to get the
+ # object to set up interfacing to other objects in the system.
+ # We return the newly-created anonymous-class object to the caller.
+ # It's expected that a considerable amount of code will depend
+ # on this behavior, so don't change it.
+ #
+ # Ok, added support for a user-defined block, 13Apr06.
+ # This leads us to an interesting choice because of the
+ # presence of the post_init call, which happens in the
+ # initialize method of the new object. We call the user's
+ # block and pass the new object to it. This is a great
+ # way to do protocol-specific initiation. It happens
+ # AFTER post_init has been called on the object, which I
+ # certainly hope is the right choice.
+ # Don't change this lightly, because accepted connections
+ # are different from connected ones and we don't want
+ # to have them behave differently with respect to post_init
+ # if at all possible.
+
+ bind_connect nil, nil, server, port, handler, *args, &blk
+ end
+
+ # This method is like {EventMachine.connect}, but allows for a local address/port
+ # to bind the connection to.
+ #
+ # @see EventMachine.connect
+ def self.bind_connect bind_addr, bind_port, server, port=nil, handler=nil, *args
+ begin
+ port = Integer(port)
+ rescue ArgumentError, TypeError
+ # there was no port, so server must be a unix domain socket
+ # the port argument is actually the handler, and the handler is one of the args
+ args.unshift handler if handler
+ handler = port
+ port = nil
+ end if port
+
+ klass = klass_from_handler(Connection, handler, *args)
+
+ s = if port
+ if bind_addr
+ bind_connect_server bind_addr, bind_port.to_i, server, port
+ else
+ connect_server server, port
+ end
+ else
+ connect_unix_server server
+ end
+
+ c = klass.new s, *args
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+ # {EventMachine.watch} registers a given file descriptor or IO object with the eventloop. The
+ # file descriptor will not be modified (it will remain blocking or non-blocking).
+ #
+ # The eventloop can be used to process readable and writable events on the file descriptor, using
+ # {EventMachine::Connection#notify_readable=} and {EventMachine::Connection#notify_writable=}
+ #
+ # {EventMachine::Connection#notify_readable?} and {EventMachine::Connection#notify_writable?} can be used
+ # to check what events are enabled on the connection.
+ #
+ # To detach the file descriptor, use {EventMachine::Connection#detach}
+ #
+ # @example
+ #
+ # module SimpleHttpClient
+ # def notify_readable
+ # header = @io.readline
+ #
+ # if header == "\r\n"
+ # # detach returns the file descriptor number (fd == @io.fileno)
+ # fd = detach
+ # end
+ # rescue EOFError
+ # detach
+ # end
+ #
+ # def unbind
+ # EM.next_tick do
+ # # socket is detached from the eventloop, but still open
+ # data = @io.read
+ # end
+ # end
+ # end
+ #
+ # EventMachine.run {
+ # sock = TCPSocket.new('site.com', 80)
+ # sock.write("GET / HTTP/1.0\r\n\r\n")
+ # conn = EventMachine.watch(sock, SimpleHttpClient)
+ # conn.notify_readable = true
+ # }
+ #
+ # @author Riham Aldakkak (eSpace Technologies)
+ def EventMachine::watch io, handler=nil, *args, &blk
+ attach_io io, true, handler, *args, &blk
+ end
+
+ # Attaches an IO object or file descriptor to the eventloop as a regular connection.
+ # The file descriptor will be set as non-blocking, and EventMachine will process
+ # receive_data and send_data events on it as it would for any other connection.
+ #
+ # To watch a fd instead, use {EventMachine.watch}, which will not alter the state of the socket
+ # and fire notify_readable and notify_writable events instead.
+ def EventMachine::attach io, handler=nil, *args, &blk
+ attach_io io, false, handler, *args, &blk
+ end
+
+ # @private
+ def EventMachine::attach_io io, watch_mode, handler=nil, *args
+ klass = klass_from_handler(Connection, handler, *args)
+
+ if !watch_mode and klass.public_instance_methods.any?{|m| [:notify_readable, :notify_writable].include? m.to_sym }
+ raise ArgumentError, "notify_readable/writable with EM.attach is not supported. Use EM.watch(io){ |c| c.notify_readable = true }"
+ end
+
+ if io.respond_to?(:fileno)
+ # getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000
+ if defined?(JRuby) && JRuby.runtime.respond_to?(:getDescriptorByFileno)
+ fd = JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel
+ else
+ fd = io.fileno
+ end
+ else
+ fd = io
+ end
+
+ s = attach_fd fd, watch_mode
+ c = klass.new s, *args
+
+ c.instance_variable_set(:@io, io)
+ c.instance_variable_set(:@watch_mode, watch_mode)
+ c.instance_variable_set(:@fd, fd)
+
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+
+ # Connect to a given host/port and re-use the provided {EventMachine::Connection} instance.
+ # Consider also {EventMachine::Connection#reconnect}.
+ #
+ # @see EventMachine::Connection#reconnect
+ def self.reconnect server, port, handler
+ # Observe, the test for already-connected FAILS if we call a reconnect inside post_init,
+ # because we haven't set up the connection in @conns by that point.
+ # RESIST THE TEMPTATION to "fix" this problem by redefining the behavior of post_init.
+ #
+ # Changed 22Nov06: if called on an already-connected handler, just return the
+ # handler and do nothing more. Originally this condition raised an exception.
+ # We may want to change it yet again and call the block, if any.
+
+ raise "invalid handler" unless handler.respond_to?(:connection_completed)
+ #raise "still connected" if @conns.has_key?(handler.signature)
+ return handler if @conns.has_key?(handler.signature)
+
+ s = if port
+ connect_server server, port
+ else
+ connect_unix_server server
+ end
+ handler.signature = s
+ @conns[s] = handler
+ block_given? and yield handler
+ handler
+ end
+
+
+ # Make a connection to a Unix-domain socket. This method is simply an alias for {.connect},
+ # which can connect to both TCP and Unix-domain sockets. Make sure that your process has sufficient
+ # permissions to open the socket it is given.
+ #
+ # @param [String] socketname Unix domain socket (local fully-qualified path) you want to connect to.
+ #
+ # @note UNIX sockets, as the name suggests, are not available on Microsoft Windows.
+ def self.connect_unix_domain socketname, *args, &blk
+ connect socketname, *args, &blk
+ end
+
+
+ # Used for UDP-based protocols. Its usage is similar to that of {EventMachine.start_server}.
+ #
+ # This method will create a new UDP (datagram) socket and
+ # bind it to the address and port that you specify.
+ # The normal callbacks (see {EventMachine.start_server}) will
+ # be called as events of interest occur on the newly-created
+ # socket, but there are some differences in how they behave.
+ #
+ # {Connection#receive_data} will be called when a datagram packet
+ # is received on the socket, but unlike TCP sockets, the message
+ # boundaries of the received data will be respected. In other words,
+ # if the remote peer sent you a datagram of a particular size,
+ # you may rely on {Connection#receive_data} to give you the
+ # exact data in the packet, with the original data length.
+ # Also observe that Connection#receive_data may be called with a
+ # *zero-length* data payload, since empty datagrams are permitted in UDP.
+ #
+ # {Connection#send_data} is available with UDP packets as with TCP,
+ # but there is an important difference. Because UDP communications
+ # are *connectionless*, there is no implicit recipient for the packets you
+ # send. Ordinarily you must specify the recipient for each packet you send.
+ # However, EventMachine provides for the typical pattern of receiving a UDP datagram
+ # from a remote peer, performing some operation, and then sending
+ # one or more packets in response to the same remote peer.
+ # To support this model easily, just use {Connection#send_data}
+ # in the code that you supply for {Connection#receive_data}.
+ #
+ # EventMachine will provide an implicit return address for any messages sent to
+ # {Connection#send_data} within the context of a {Connection#receive_data} callback,
+ # and your response will automatically go to the correct remote peer.
+ #
+ # Observe that the port number that you supply to {EventMachine.open_datagram_socket}
+ # may be zero. In this case, EventMachine will create a UDP socket
+ # that is bound to an [ephemeral port](http://en.wikipedia.org/wiki/Ephemeral_port).
+ # This is not appropriate for servers that must publish a well-known
+ # port to which remote peers may send datagrams. But it can be useful
+ # for clients that send datagrams to other servers.
+ # If you do this, you will receive any responses from the remote
+ # servers through the normal {Connection#receive_data} callback.
+ # Observe that you will probably have issues with firewalls blocking
+ # the ephemeral port numbers, so this technique is most appropriate for LANs.
+ #
+ # If you wish to send datagrams to arbitrary remote peers (not
+ # necessarily ones that have sent data to which you are responding),
+ # then see {Connection#send_datagram}.
+ #
+ # DO NOT call send_data from a datagram socket outside of a {Connection#receive_data} method. Use {Connection#send_datagram}.
+ # If you do use {Connection#send_data} outside of a {Connection#receive_data} method, you'll get a confusing error
+ # because there is no "peer," as #send_data requires (inside of {EventMachine::Connection#receive_data},
+ # {EventMachine::Connection#send_data} "fakes" the peer as described above).
+ #
+ # @param [String] address IP address
+ # @param [String] port Port
+ # @param [Class, Module] handler A class or a module that implements connection lifecycle callbacks.
+ def self.open_datagram_socket address, port, handler=nil, *args
+ # Replaced the implementation on 01Oct06. Thanks to Tobias Gustafsson for pointing
+ # out that this originally did not take a class but only a module.
+
+
+ klass = klass_from_handler(Connection, handler, *args)
+ s = open_udp_socket address, port.to_i
+ c = klass.new s, *args
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+
+ # For advanced users. This function sets the default timer granularity, which by default is
+ # slightly smaller than 100 milliseconds. Call this function to set a higher or lower granularity.
+ # The function affects the behavior of {EventMachine.add_timer} and {EventMachine.add_periodic_timer}.
+ # Most applications will not need to call this function.
+ #
+ # Avoid setting the quantum to very low values because that may reduce performance under some extreme conditions.
+ # We recommend that you not use values lower than 10.
+ #
+ # This method only can be used if event loop is running.
+ #
+ # @param [Integer] mills New timer granularity, in milliseconds
+ #
+ # @see EventMachine.add_timer
+ # @see EventMachine.add_periodic_timer
+ # @see EventMachine::Timer
+ # @see EventMachine.run
+ def self.set_quantum mills
+ set_timer_quantum mills.to_i
+ end
+
+ # Sets the maximum number of timers and periodic timers that may be outstanding at any
+ # given time. You only need to call {.set_max_timers} if you need more than the default
+ # number of timers, which on most platforms is 1000.
+ #
+ # @note This method has to be used *before* event loop is started.
+ #
+ # @param [Integer] ct Maximum number of timers that may be outstanding at any given time
+ #
+ # @see EventMachine.add_timer
+ # @see EventMachine.add_periodic_timer
+ # @see EventMachine::Timer
+ def self.set_max_timers ct
+ set_max_timer_count ct
+ end
+
+ # Gets the current maximum number of allowed timers
+ #
+ # @return [Integer] Maximum number of timers that may be outstanding at any given time
+ def self.get_max_timers
+ get_max_timer_count
+ end
+
+ # Returns the total number of connections (file descriptors) currently held by the reactor.
+ # Note that a tick must pass after the 'initiation' of a connection for this number to increment.
+ # It's usually accurate, but don't rely on the exact precision of this number unless you really know EM internals.
+ #
+ # @example
+ #
+ # EventMachine.run {
+ # EventMachine.connect("rubyeventmachine.com", 80)
+ # # count will be 0 in this case, because connection is not
+ # # established yet
+ # count = EventMachine.connection_count
+ # }
+ #
+ #
+ # @example
+ #
+ # EventMachine.run {
+ # EventMachine.connect("rubyeventmachine.com", 80)
+ #
+ # EventMachine.next_tick {
+ # # In this example, count will be 1 since the connection has been established in
+ # # the next loop of the reactor.
+ # count = EventMachine.connection_count
+ # }
+ # }
+ #
+ # @return [Integer] Number of connections currently held by the reactor.
+ def self.connection_count
+ self.get_connection_count
+ end
+
+ # The is the responder for the loopback-signalled event.
+ # It can be fired either by code running on a separate thread ({EventMachine.defer}) or on
+ # the main thread ({EventMachine.next_tick}).
+ # It will often happen that a next_tick handler will reschedule itself. We
+ # consume a copy of the tick queue so that tick events scheduled by tick events
+ # have to wait for the next pass through the reactor core.
+ #
+ # @private
+ def self.run_deferred_callbacks
+ until (@resultqueue ||= []).empty?
+ result,cback = @resultqueue.pop
+ cback.call result if cback
+ end
+
+ # Capture the size at the start of this tick...
+ size = @next_tick_mutex.synchronize { @next_tick_queue.size }
+ size.times do |i|
+ callback = @next_tick_mutex.synchronize { @next_tick_queue.shift }
+ begin
+ callback.call
+ rescue
+ exception_raised = true
+ raise
+ ensure
+ # This is a little nasty. The problem is, if an exception occurs during
+ # the callback, then we need to send a signal to the reactor to actually
+ # do some work during the next_tick. The only mechanism we have from the
+ # ruby side is next_tick itself, although ideally, we'd just drop a byte
+ # on the loopback descriptor.
+ next_tick {} if exception_raised
+ end
+ end
+ end
+
+
+ # EventMachine.defer is used for integrating blocking operations into EventMachine's control flow.
+ # The action of {.defer} is to take the block specified in the first parameter (the "operation")
+ # and schedule it for asynchronous execution on an internal thread pool maintained by EventMachine.
+ # When the operation completes, it will pass the result computed by the block (if any) back to the
+ # EventMachine reactor. Then, EventMachine calls the block specified in the second parameter to
+ # {.defer} (the "callback"), as part of its normal event handling loop. The result computed by the
+ # operation block is passed as a parameter to the callback. You may omit the callback parameter if
+ # you don't need to execute any code after the operation completes. If the operation raises an
+ # unhandled exception, the exception will be passed to the third parameter to {.defer} (the
+ # "errback"), as part of its normal event handling loop. If no errback is provided, the exception
+ # will be allowed to blow through to the main thread immediately.
+ #
+ # ## Caveats ##
+ #
+ # Note carefully that the code in your deferred operation will be executed on a separate
+ # thread from the main EventMachine processing and all other Ruby threads that may exist in
+ # your program. Also, multiple deferred operations may be running at once! Therefore, you
+ # are responsible for ensuring that your operation code is threadsafe.
+ #
+ # Don't write a deferred operation that will block forever. If so, the current implementation will
+ # not detect the problem, and the thread will never be returned to the pool. EventMachine limits
+ # the number of threads in its pool, so if you do this enough times, your subsequent deferred
+ # operations won't get a chance to run.
+ #
+ # The threads within the EventMachine's thread pool have abort_on_exception set to true. As a result,
+ # if an unhandled exception is raised by the deferred operation and an errback is not provided, it
+ # will blow through to the main thread immediately. If the main thread is within an indiscriminate
+ # rescue block at that time, the exception could be handled improperly by the main thread.
+ #
+ # @example
+ #
+ # operation = proc {
+ # # perform a long-running operation here, such as a database query.
+ # "result" # as usual, the last expression evaluated in the block will be the return value.
+ # }
+ # callback = proc {|result|
+ # # do something with result here, such as send it back to a network client.
+ # }
+ # errback = proc {|error|
+ # # do something with error here, such as re-raising or logging.
+ # }
+ #
+ # EventMachine.defer(operation, callback, errback)
+ #
+ # @param [#call] op An operation you want to offload to EventMachine thread pool
+ # @param [#call] callback A callback that will be run on the event loop thread after `operation` finishes.
+ # @param [#call] errback An errback that will be run on the event loop thread after `operation` raises an exception.
+ #
+ # @see EventMachine.threadpool_size
+ def self.defer op = nil, callback = nil, errback = nil, &blk
+ # OBSERVE that #next_tick hacks into this mechanism, so don't make any changes here
+ # without syncing there.
+ #
+ # Running with $VERBOSE set to true gives a warning unless all ivars are defined when
+ # they appear in rvalues. But we DON'T ever want to initialize @threadqueue unless we
+ # need it, because the Ruby threads are so heavyweight. We end up with this bizarre
+ # way of initializing @threadqueue because EventMachine is a Module, not a Class, and
+ # has no constructor.
+
+ unless @threadpool
+ @threadpool = []
+ @threadqueue = ::Queue.new
+ @resultqueue = ::Queue.new
+ spawn_threadpool
+ end
+
+ @threadqueue << [op||blk,callback,errback]
+ end
+
+
+ # @private
+ def self.spawn_threadpool
+ until @threadpool.size == @threadpool_size.to_i
+ thread = Thread.new do
+ Thread.current.abort_on_exception = true
+ while true
+ begin
+ op, cback, eback = *@threadqueue.pop
+ rescue ThreadError
+ $stderr.puts $!.message
+ break # Ruby 2.0 may fail at Queue.pop
+ end
+ begin
+ result = op.call
+ @resultqueue << [result, cback]
+ rescue Exception => error
+ raise error unless eback
+ @resultqueue << [error, eback]
+ end
+ signal_loopbreak
+ end
+ end
+ @threadpool << thread
+ end
+ @all_threads_spawned = true
+ end
+
+ ##
+ # Returns +true+ if all deferred actions are done executing and their
+ # callbacks have been fired.
+ #
+ def self.defers_finished?
+ return false if @threadpool and !@all_threads_spawned
+ return false if @threadqueue and not @threadqueue.empty?
+ return false if @resultqueue and not @resultqueue.empty?
+ return false if @threadpool and @threadqueue.num_waiting != @threadpool.size
+ return true
+ end
+
+ class << self
+ # @private
+ attr_reader :threadpool
+
+ # Size of the EventMachine.defer threadpool (defaults to 20)
+ # @return [Number]
+ attr_accessor :threadpool_size
+ EventMachine.threadpool_size = 20
+ end
+
+ # Schedules a proc for execution immediately after the next "turn" through the reactor
+ # core. An advanced technique, this can be useful for improving memory management and/or
+ # application responsiveness, especially when scheduling large amounts of data for
+ # writing to a network connection.
+ #
+ # This method takes either a single argument (which must be a callable object) or a block.
+ #
+ # @param [#call] pr A callable object to run
+ def self.next_tick pr=nil, &block
+ # This works by adding to the @resultqueue that's used for #defer.
+ # The general idea is that next_tick is used when we want to give the reactor a chance
+ # to let other operations run, either to balance the load out more evenly, or to let
+ # outbound network buffers drain, or both. So we probably do NOT want to block, and
+ # we probably do NOT want to be spinning any threads. A program that uses next_tick
+ # but not #defer shouldn't suffer the penalty of having Ruby threads running. They're
+ # extremely expensive even if they're just sleeping.
+
+ raise ArgumentError, "no proc or block given" unless ((pr && pr.respond_to?(:call)) or block)
+ @next_tick_mutex.synchronize do
+ @next_tick_queue << ( pr || block )
+ end
+ signal_loopbreak if reactor_running?
+ end
+
+ # A wrapper over the setuid system call. Particularly useful when opening a network
+ # server on a privileged port because you can use this call to drop privileges
+ # after opening the port. Also very useful after a call to {.set_descriptor_table_size},
+ # which generally requires that you start your process with root privileges.
+ #
+ # This method is intended for use in enforcing security requirements, consequently
+ # it will throw a fatal error and end your program if it fails.
+ #
+ # @param [String] username The effective name of the user whose privilege-level your process should attain.
+ #
+ # @note This method has no effective implementation on Windows or in the pure-Ruby
+ # implementation of EventMachine
+ def self.set_effective_user username
+ EventMachine::setuid_string username
+ end
+
+
+ # Sets the maximum number of file or socket descriptors that your process may open.
+ # If you call this method with no arguments, it will simply return
+ # the current size of the descriptor table without attempting to change it.
+ #
+ # The new limit on open descriptors **only** applies to sockets and other descriptors
+ # that belong to EventMachine. It has **no effect** on the number of descriptors
+ # you can create in ordinary Ruby code.
+ #
+ # Not available on all platforms. Increasing the number of descriptors beyond its
+ # default limit usually requires superuser privileges. (See {.set_effective_user}
+ # for a way to drop superuser privileges while your program is running.)
+ #
+ # @param [Integer] n_descriptors The maximum number of file or socket descriptors that your process may open
+ # @return [Integer] The new descriptor table size.
+ def self.set_descriptor_table_size n_descriptors=nil
+ EventMachine::set_rlimit_nofile n_descriptors
+ end
+
+
+
+ # Runs an external process.
+ #
+ # @example
+ #
+ # module RubyCounter
+ # def post_init
+ # # count up to 5
+ # send_data "5\n"
+ # end
+ # def receive_data data
+ # puts "ruby sent me: #{data}"
+ # end
+ # def unbind
+ # puts "ruby died with exit status: #{get_status.exitstatus}"
+ # end
+ # end
+ #
+ # EventMachine.run {
+ # EventMachine.popen("ruby -e' $stdout.sync = true; gets.to_i.times{ |i| puts i+1; sleep 1 } '", RubyCounter)
+ # }
+ #
+ # @note This method is not supported on Microsoft Windows
+ # @see EventMachine::DeferrableChildProcess
+ # @see EventMachine.system
+ def self.popen cmd, handler=nil, *args
+ # At this moment, it's only available on Unix.
+ # Perhaps misnamed since the underlying function uses socketpair and is full-duplex.
+
+ klass = klass_from_handler(Connection, handler, *args)
+ w = case cmd
+ when Array
+ cmd
+ when String
+ Shellwords::shellwords( cmd )
+ end
+ w.unshift( w.first ) if w.first
+ s = invoke_popen( w )
+ c = klass.new s, *args
+ @conns[s] = c
+ yield(c) if block_given?
+ c
+ end
+
+
+ # Tells you whether the EventMachine reactor loop is currently running.
+ #
+ # Useful when writing libraries that want to run event-driven code, but may
+ # be running in programs that are already event-driven. In such cases, if {EventMachine.reactor_running?}
+ # returns false, your code can invoke {EventMachine.run} and run your application code inside
+ # the block passed to that method. If this method returns true, just
+ # execute your event-aware code.
+ #
+ # @return [Boolean] true if the EventMachine reactor loop is currently running
+ def self.reactor_running?
+ @reactor_running && Process.pid == @reactor_pid
+ end
+
+
+ # (Experimental)
+ #
+ # @private
+ def self.open_keyboard handler=nil, *args
+ klass = klass_from_handler(Connection, handler, *args)
+
+ s = read_keyboard
+ c = klass.new s, *args
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+ # EventMachine's file monitoring API. Currently supported are the following events
+ # on individual files, using inotify on Linux systems, and kqueue for *BSD and Mac OS X:
+ #
+ # * File modified (written to)
+ # * File moved/renamed
+ # * File deleted
+ #
+ # EventMachine::watch_file takes a filename and a handler Module containing your custom callback methods.
+ # This will setup the low level monitoring on the specified file, and create a new EventMachine::FileWatch
+ # object with your Module mixed in. FileWatch is a subclass of {EventMachine::Connection}, so callbacks on this object
+ # work in the familiar way. The callbacks that will be fired by EventMachine are:
+ #
+ # * file_modified
+ # * file_moved
+ # * file_deleted
+ #
+ # You can access the filename being monitored from within this object using {FileWatch#path}.
+ #
+ # When a file is deleted, {FileWatch#stop_watching} will be called after your file_deleted callback,
+ # to clean up the underlying monitoring and remove EventMachine's reference to the now-useless {FileWatch} instance.
+ # This will in turn call unbind, if you wish to use it.
+ #
+ # The corresponding system-level Errno will be raised when attempting to monitor non-existent files,
+ # files with wrong permissions, or if an error occurs dealing with inotify/kqueue.
+ #
+ # @example
+ #
+ # # Before running this example, make sure we have a file to monitor:
+ # # $ echo "bar" > /tmp/foo
+ #
+ # module Handler
+ # def file_modified
+ # puts "#{path} modified"
+ # end
+ #
+ # def file_moved
+ # puts "#{path} moved"
+ # end
+ #
+ # def file_deleted
+ # puts "#{path} deleted"
+ # end
+ #
+ # def unbind
+ # puts "#{path} monitoring ceased"
+ # end
+ # end
+ #
+ # # for efficient file watching, use kqueue on Mac OS X
+ # EventMachine.kqueue = true if EventMachine.kqueue?
+ #
+ # EventMachine.run {
+ # EventMachine.watch_file("/tmp/foo", Handler)
+ # }
+ #
+ # # $ echo "baz" >> /tmp/foo => "/tmp/foo modified"
+ # # $ mv /tmp/foo /tmp/oof => "/tmp/foo moved"
+ # # $ rm /tmp/oof => "/tmp/foo deleted"
+ #
+ # @note The ability to pick up on the new filename after a rename is not yet supported.
+ # Calling #path will always return the filename you originally used.
+ #
+ # @param [String] filename Local path to the file to watch.
+ # @param [Class, Module] handler A class or module that implements event handlers associated with the file.
+ def self.watch_file(filename, handler=nil, *args)
+ klass = klass_from_handler(FileWatch, handler, *args)
+
+ s = EM::watch_filename(filename)
+ c = klass.new s, *args
+ # we have to set the path like this because of how Connection.new works
+ c.instance_variable_set("@path", filename)
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+ # EventMachine's process monitoring API. On Mac OS X and *BSD this method is implemented using kqueue.
+ #
+ # @example
+ #
+ # module ProcessWatcher
+ # def process_exited
+ # put 'the forked child died!'
+ # end
+ # end
+ #
+ # pid = fork{ sleep }
+ #
+ # EventMachine.run {
+ # EventMachine.watch_process(pid, ProcessWatcher)
+ # EventMachine.add_timer(1){ Process.kill('TERM', pid) }
+ # }
+ #
+ # @param [Integer] pid PID of the process to watch.
+ # @param [Class, Module] handler A class or module that implements event handlers associated with the file.
+ def self.watch_process(pid, handler=nil, *args)
+ pid = pid.to_i
+
+ klass = klass_from_handler(ProcessWatch, handler, *args)
+
+ s = EM::watch_pid(pid)
+ c = klass.new s, *args
+ # we have to set the path like this because of how Connection.new works
+ c.instance_variable_set("@pid", pid)
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+ # Catch-all for errors raised during event loop callbacks.
+ #
+ # @example
+ #
+ # EventMachine.error_handler{ |e|
+ # puts "Error raised during event loop: #{e.message}"
+ # }
+ #
+ # @param [#call] cb Global catch-all errback
+ def self.error_handler cb = nil, &blk
+ if cb or blk
+ @error_handler = cb || blk
+ elsif instance_variable_defined? :@error_handler
+ remove_instance_variable :@error_handler
+ end
+ end
+
+ # This method allows for direct writing of incoming data back out to another descriptor, at the C++ level in the reactor.
+ # This is very efficient and especially useful for proxies where high performance is required. Propogating data from a server response
+ # all the way up to Ruby, and then back down to the reactor to be sent back to the client, is often unnecessary and
+ # incurs a significant performance decrease.
+ #
+ # The two arguments are instance of {EventMachine::Connection} subclasses, 'from' and 'to'. 'from' is the connection whose inbound data you want
+ # relayed back out. 'to' is the connection to write it to.
+ #
+ # Once you call this method, the 'from' connection will no longer get receive_data callbacks from the reactor,
+ # except in the case that 'to' connection has already closed when attempting to write to it. You can see
+ # in the example, that proxy_target_unbound will be called when this occurs. After that, further incoming
+ # data will be passed into receive_data as normal.
+ #
+ # Note also that this feature supports different types of descriptors: TCP, UDP, and pipes. You can relay
+ # data from one kind to another, for example, feed a pipe from a UDP stream.
+ #
+ # @example
+ #
+ # module ProxyConnection
+ # def initialize(client, request)
+ # @client, @request = client, request
+ # end
+ #
+ # def post_init
+ # EM::enable_proxy(self, @client)
+ # end
+ #
+ # def connection_completed
+ # send_data @request
+ # end
+ #
+ # def proxy_target_unbound
+ # close_connection
+ # end
+ #
+ # def unbind
+ # @client.close_connection_after_writing
+ # end
+ # end
+ #
+ # module ProxyServer
+ # def receive_data(data)
+ # (@buf ||= "") << data
+ # if @buf =~ /\r\n\r\n/ # all http headers received
+ # EventMachine.connect("10.0.0.15", 80, ProxyConnection, self, data)
+ # end
+ # end
+ # end
+ #
+ # EventMachine.run {
+ # EventMachine.start_server("127.0.0.1", 8080, ProxyServer)
+ # }
+ #
+ # @param [EventMachine::Connection] from Source of data to be proxies/streamed.
+ # @param [EventMachine::Connection] to Destination of data to be proxies/streamed.
+ # @param [Integer] bufsize Buffer size to use
+ # @param [Integer] length Maximum number of bytes to proxy.
+ #
+ # @see EventMachine.disable_proxy
+ def self.enable_proxy(from, to, bufsize=0, length=0)
+ EM::start_proxy(from.signature, to.signature, bufsize, length)
+ end
+
+ # Takes just one argument, a {Connection} that has proxying enabled via {EventMachine.enable_proxy}.
+ # Calling this method will remove that functionality and your connection will begin receiving
+ # data via {Connection#receive_data} again.
+ #
+ # @param [EventMachine::Connection] from Source of data that is being proxied
+ # @see EventMachine.enable_proxy
+ def self.disable_proxy(from)
+ EM::stop_proxy(from.signature)
+ end
+
+ # Retrieve the heartbeat interval. This is how often EventMachine will check for dead connections
+ # that have had an inactivity timeout set via {Connection#set_comm_inactivity_timeout}.
+ # Default is 2 seconds.
+ #
+ # @return [Integer] Heartbeat interval, in seconds
+ def self.heartbeat_interval
+ EM::get_heartbeat_interval
+ end
+
+ # Set the heartbeat interval. This is how often EventMachine will check for dead connections
+ # that have had an inactivity timeout set via {Connection#set_comm_inactivity_timeout}.
+ # Takes a Numeric number of seconds. Default is 2.
+ #
+ # @param [Integer] time Heartbeat interval, in seconds
+ def self.heartbeat_interval=(time)
+ EM::set_heartbeat_interval time.to_f
+ end
+
+ # @private
+ def self.event_callback conn_binding, opcode, data
+ #
+ # Changed 27Dec07: Eliminated the hookable error handling.
+ # No one was using it, and it degraded performance significantly.
+ # It's in original_event_callback, which is dead code.
+ #
+ # Changed 25Jul08: Added a partial solution to the problem of exceptions
+ # raised in user-written event-handlers. If such exceptions are not caught,
+ # we must cause the reactor to stop, and then re-raise the exception.
+ # Otherwise, the reactor doesn't stop and it's left on the call stack.
+ # This is partial because we only added it to #unbind, where it's critical
+ # (to keep unbind handlers from being re-entered when a stopping reactor
+ # runs down open connections). It should go on the other calls to user
+ # code, but the performance impact may be too large.
+ #
+ if opcode == ConnectionUnbound
+ if c = @conns.delete( conn_binding )
+ begin
+ if c.original_method(:unbind).arity != 0
+ c.unbind(data == 0 ? nil : EventMachine::ERRNOS[data])
+ else
+ c.unbind
+ end
+ # If this is an attached (but not watched) connection, close the underlying io object.
+ if c.instance_variable_defined?(:@io) and !c.instance_variable_get(:@watch_mode)
+ io = c.instance_variable_get(:@io)
+ begin
+ io.close
+ rescue Errno::EBADF, IOError
+ end
+ end
+ # As noted above, unbind absolutely must not raise an exception or the reactor will crash.
+ # If there is no EM.error_handler, or if the error_handler retrows, then stop the reactor,
+ # stash the exception in $wrapped_exception, and the exception will be raised after the
+ # reactor is cleaned up (see the last line of self.run).
+ rescue Exception => error
+ if instance_variable_defined? :@error_handler
+ begin
+ @error_handler.call error
+ # No need to stop unless error_handler rethrows
+ rescue Exception => error
+ @wrapped_exception = error
+ stop
+ end
+ else
+ @wrapped_exception = error
+ stop
+ end
+ end
+ elsif c = @acceptors.delete( conn_binding )
+ # no-op
+ else
+ if $! # Bubble user generated errors.
+ @wrapped_exception = $!
+ stop
+ else
+ raise ConnectionNotBound, "received ConnectionUnbound for an unknown signature: #{conn_binding}"
+ end
+ end
+ elsif opcode == ConnectionAccepted
+ accep,args,blk = @acceptors[conn_binding]
+ raise NoHandlerForAcceptedConnection unless accep
+ c = accep.new data, *args
+ @conns[data] = c
+ blk and blk.call(c)
+ c # (needed?)
+ ##
+ # The remaining code is a fallback for the pure ruby and java reactors.
+ # In the C++ reactor, these events are handled in the C event_callback() in rubymain.cpp
+ elsif opcode == ConnectionCompleted
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
+ c.connection_completed
+ elsif opcode == SslHandshakeCompleted
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received SslHandshakeCompleted for unknown signature: #{conn_binding}"
+ c.ssl_handshake_completed
+ elsif opcode == SslVerify
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received SslVerify for unknown signature: #{conn_binding}"
+ c.close_connection if c.ssl_verify_peer(data) == false
+ elsif opcode == TimerFired
+ t = @timers.delete( data )
+ return if t == false # timer cancelled
+ t or raise UnknownTimerFired, "timer data: #{data}"
+ t.call
+ elsif opcode == ConnectionData
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received data #{data} for unknown signature: #{conn_binding}"
+ c.receive_data data
+ elsif opcode == LoopbreakSignalled
+ run_deferred_callbacks
+ elsif opcode == ConnectionNotifyReadable
+ c = @conns[conn_binding] or raise ConnectionNotBound
+ c.notify_readable
+ elsif opcode == ConnectionNotifyWritable
+ c = @conns[conn_binding] or raise ConnectionNotBound
+ c.notify_writable
+ end
+ end
+
+ #
+ #
+ # @private
+ def self._open_file_for_writing filename, handler=nil
+ klass = klass_from_handler(Connection, handler)
+
+ s = _write_file filename
+ c = klass.new s
+ @conns[s] = c
+ block_given? and yield c
+ c
+ end
+
+ # @private
+ def self.klass_from_handler(klass = Connection, handler = nil, *args)
+ klass = if handler and handler.is_a?(Class)
+ raise ArgumentError, "must provide module or subclass of #{klass.name}" unless klass >= handler
+ handler
+ elsif handler
+ if defined?(handler::EM_CONNECTION_CLASS)
+ handler::EM_CONNECTION_CLASS
+ else
+ handler::const_set(:EM_CONNECTION_CLASS, Class.new(klass) {include handler})
+ end
+ else
+ klass
+ end
+
+ arity = klass.instance_method(:initialize).arity
+ expected = arity >= 0 ? arity : -(arity + 1)
+ if (arity >= 0 and args.size != expected) or (arity < 0 and args.size < expected)
+ raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size} for #{expected})"
+ end
+
+ klass
+ end
+end # module EventMachine
+
+# Alias for {EventMachine}
+EM = EventMachine
+# Alias for {EventMachine::Protocols}
+EM::P = EventMachine::Protocols
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/fastfilereaderext.bundle b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/fastfilereaderext.bundle
new file mode 100755
index 0000000..2cf4e83
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/fastfilereaderext.bundle
Binary files differ
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/jeventmachine.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/jeventmachine.rb
new file mode 100644
index 0000000..21a267d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/jeventmachine.rb
@@ -0,0 +1,316 @@
+#--
+#
+# Author:: Francis Cianfrocca (gmail: blackhedd)
+# Homepage:: http://rubyeventmachine.com
+# Date:: 8 Apr 2006
+#
+# See EventMachine and EventMachine::Connection for documentation and
+# usage examples.
+#
+#----------------------------------------------------------------------------
+#
+# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+# Gmail: blackhedd
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of either: 1) the GNU General Public License
+# as published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version; or 2) Ruby's License.
+#
+# See the file COPYING for complete licensing information.
+#
+#---------------------------------------------------------------------------
+#
+#
+
+# This module provides "glue" for the Java version of the EventMachine reactor core.
+# For C++ EventMachines, the analogous functionality is found in ext/rubymain.cpp,
+# which is a garden-variety Ruby-extension glue module.
+
+require 'java'
+require 'rubyeventmachine'
+require 'socket'
+
+java_import java.io.FileDescriptor
+java_import java.nio.channels.SocketChannel
+java_import java.lang.reflect.Field
+
+module JavaFields
+ def set_field(key, value)
+ field = getClass.getDeclaredField(key)
+ field.setAccessible(true)
+ if field.getType.toString == 'int'
+ field.setInt(self, value)
+ else
+ field.set(self, value)
+ end
+ end
+
+ def get_field(key)
+ field = getClass.getDeclaredField(key)
+ field.setAccessible(true)
+ field.get(self)
+ end
+end
+
+FileDescriptor.send :include, JavaFields
+SocketChannel.send :include, JavaFields
+
+module EventMachine
+ # TODO: These event numbers are defined in way too many places.
+ # DRY them up.
+ # @private
+ TimerFired = 100
+ # @private
+ ConnectionData = 101
+ # @private
+ ConnectionUnbound = 102
+ # @private
+ ConnectionAccepted = 103
+ # @private
+ ConnectionCompleted = 104
+ # @private
+ LoopbreakSignalled = 105
+ # @private
+ ConnectionNotifyReadable = 106
+ # @private
+ ConnectionNotifyWritable = 107
+ # @private
+ SslHandshakeCompleted = 108
+ # @private
+ SslVerify = 109
+
+ # @private
+ EM_PROTO_SSLv2 = 2
+ # @private
+ EM_PROTO_SSLv3 = 4
+ # @private
+ EM_PROTO_TLSv1 = 8
+ # @private
+ EM_PROTO_TLSv1_1 = 16
+ # @private
+ EM_PROTO_TLSv1_2 = 32
+
+ # Exceptions that are defined in rubymain.cpp
+ class ConnectionError < RuntimeError; end
+ class ConnectionNotBound < RuntimeError; end
+ class UnknownTimerFired < RuntimeError; end
+ class Unsupported < RuntimeError; end
+
+ # This thunk class used to be called EM, but that caused conflicts with
+ # the alias "EM" for module EventMachine. (FC, 20Jun08)
+ class JEM < com.rubyeventmachine.EmReactor
+ def eventCallback a1, a2, a3, a4
+ s = String.from_java_bytes(a3.array[a3.position...a3.limit]) if a3
+ EventMachine::event_callback a1, a2, s || a4
+ nil
+ end
+ end
+ # class Connection < com.rubyeventmachine.Connection
+ # def associate_callback_target sig
+ # # No-op for the time being.
+ # end
+ # end
+ def self.initialize_event_machine
+ @em = JEM.new
+ end
+ def self.release_machine
+ @em = nil
+ end
+ def self.add_oneshot_timer interval
+ @em.installOneshotTimer interval
+ end
+ def self.run_machine
+ @em.run
+ end
+ def self.stop
+ @em.stop
+ end
+ def self.start_tcp_server server, port
+ @em.startTcpServer server, port
+ end
+ def self.stop_tcp_server sig
+ @em.stopTcpServer sig
+ end
+ def self.start_unix_server filename
+ # TEMPORARILY unsupported until someone figures out how to do it.
+ raise "unsupported on this platform"
+ end
+ def self.send_data sig, data, length
+ @em.sendData sig, data.to_java_bytes
+ rescue java.lang.NullPointerException
+ 0
+ end
+ def self.send_datagram sig, data, length, address, port
+ @em.sendDatagram sig, data.to_java_bytes, length, address, port
+ end
+ def self.connect_server server, port
+ bind_connect_server nil, nil, server, port
+ end
+ def self.bind_connect_server bind_addr, bind_port, server, port
+ @em.connectTcpServer bind_addr, bind_port.to_i, server, port
+ end
+ def self.close_connection sig, after_writing
+ @em.closeConnection sig, after_writing
+ end
+ def self.set_comm_inactivity_timeout sig, interval
+ @em.setCommInactivityTimeout sig, interval
+ end
+ def self.set_pending_connect_timeout sig, val
+ end
+ def self.set_heartbeat_interval val
+ end
+ def self.start_tls sig
+ @em.startTls sig
+ end
+ def self.ssl?
+ false
+ end
+ def self.signal_loopbreak
+ @em.signalLoopbreak
+ end
+ def self.set_timer_quantum q
+ @em.setTimerQuantum q
+ end
+ def self.epoll
+ # Epoll is a no-op for Java.
+ # The latest Java versions run epoll when possible in NIO.
+ end
+ def self.epoll= val
+ end
+ def self.kqueue
+ end
+ def self.kqueue= val
+ end
+ def self.epoll?
+ false
+ end
+ def self.kqueue?
+ false
+ end
+ def self.set_rlimit_nofile n_descriptors
+ # Currently a no-op for Java.
+ end
+ def self.open_udp_socket server, port
+ @em.openUdpSocket server, port
+ end
+ def self.invoke_popen cmd
+ # TEMPORARILY unsupported until someone figures out how to do it.
+ raise "unsupported on this platform"
+ end
+ def self.read_keyboard
+ # TEMPORARILY unsupported until someone figures out how to do it.
+ raise "temporarily unsupported on this platform"
+ end
+ def self.set_max_timer_count num
+ # harmless no-op in Java. There's no built-in timer limit.
+ @max_timer_count = num
+ end
+ def self.get_max_timer_count
+ # harmless no-op in Java. There's no built-in timer limit.
+ @max_timer_count || 100_000
+ end
+ def self.library_type
+ :java
+ end
+ def self.get_peername sig
+ if peer = @em.getPeerName(sig)
+ Socket.pack_sockaddr_in(*peer)
+ end
+ end
+ def self.get_sockname sig
+ if sockName = @em.getSockName(sig)
+ Socket.pack_sockaddr_in(*sockName)
+ end
+ end
+ # @private
+ def self.attach_fd fileno, watch_mode
+ # 3Aug09: We could pass in the actual SocketChannel, but then it would be modified (set as non-blocking), and
+ # we would need some logic to make sure detach_fd below didn't clobber it. For now, we just always make a new
+ # SocketChannel for the underlying file descriptor
+ # if fileno.java_kind_of? SocketChannel
+ # ch = fileno
+ # ch.configureBlocking(false)
+ # fileno = nil
+ # elsif fileno.java_kind_of? java.nio.channels.Channel
+
+ if fileno.java_kind_of? java.nio.channels.Channel
+ field = fileno.getClass.getDeclaredField('fdVal')
+ field.setAccessible(true)
+ fileno = field.get(fileno)
+ else
+ raise ArgumentError, 'attach_fd requires Java Channel or POSIX fileno' unless fileno.is_a? Integer
+ end
+
+ if fileno == 0
+ raise "can't open STDIN as selectable in Java =("
+ elsif fileno.is_a? Integer
+ # 8Aug09: The following code is specific to the sun jvm's SocketChannelImpl. Is there a cross-platform
+ # way of implementing this? If so, also remember to update EventableSocketChannel#close and #cleanup
+ fd = FileDescriptor.new
+ fd.set_field 'fd', fileno
+
+ ch = SocketChannel.open
+ ch.configureBlocking(false)
+ ch.kill
+ ch.set_field 'fd', fd
+ ch.set_field 'fdVal', fileno
+ ch.set_field 'state', ch.get_field('ST_CONNECTED')
+ end
+
+ @em.attachChannel(ch,watch_mode)
+ end
+ def self.detach_fd sig
+ if ch = @em.detachChannel(sig)
+ ch.get_field 'fdVal'
+ end
+ end
+
+ def self.set_notify_readable sig, mode
+ @em.setNotifyReadable(sig, mode)
+ end
+ def self.set_notify_writable sig, mode
+ @em.setNotifyWritable(sig, mode)
+ end
+
+ def self.is_notify_readable sig
+ @em.isNotifyReadable(sig)
+ end
+ def self.is_notify_writable sig
+ @em.isNotifyWritable(sig)
+ end
+ def self.get_connection_count
+ @em.getConnectionCount
+ end
+
+ def self.pause_connection(sig)
+ @em.pauseConnection(sig)
+ end
+ def self.resume_connection(sig)
+ @em.resumeConnection(sig)
+ end
+ def self.connection_paused?(sig)
+ @em.isConnectionPaused(sig)
+ end
+ def self._get_outbound_data_size(sig)
+ @em.getOutboundDataSize(sig)
+ end
+
+
+ def self.set_tls_parms(sig, params)
+ end
+ def self.start_tls(sig)
+ end
+ def self.send_file_data(sig, filename)
+ end
+
+ class Connection
+ def associate_callback_target sig
+ # No-op for the time being
+ end
+ def get_outbound_data_size
+ EM._get_outbound_data_size @signature
+ end
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/rubyeventmachine.bundle b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/rubyeventmachine.bundle
new file mode 100755
index 0000000..3967d9a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/lib/rubyeventmachine.bundle
Binary files differ