summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7')
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/CHANGELOG.md179
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/GNU281
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/LICENSE60
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/README.md110
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/DocumentationGuidesIndex.md27
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/GettingStarted.md521
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/ChangeLog211
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES246
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/EPOLL141
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/INSTALL13
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/KEYBOARD42
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LEGAL25
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LIGHTWEIGHT_CONCURRENCY130
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/PURE_RUBY75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/RELEASE_NOTES94
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SMTP4
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SPAWNED_PROCESSES148
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/TODO8
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/01_eventmachine_echo_server.rb18
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb22
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/03_simple_chat_server.rb149
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/04_simple_chat_server_step_one.rb27
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/05_simple_chat_server_step_two.rb43
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/06_simple_chat_server_step_three.rb98
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/07_simple_chat_server_step_four.rb121
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/08_simple_chat_server_step_five.rb141
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_channel.rb43
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_queue.rb2
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_array.rb15
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_counter.rb32
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/helper.rb2
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/Makefile273
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.cpp124
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.h52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/cmain.cpp988
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.cpp2096
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.h457
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.cpp2396
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.h308
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/eventmachine.h143
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/extconf.rb270
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/Makefile273
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/extconf.rb109
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.cpp214
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.h59
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/rubymain.cpp126
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/kb.cpp79
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.cpp107
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.h51
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/pipe.cpp354
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/project.h174
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/rubymain.cpp1515
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.cpp619
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.h103
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.classpath8
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.project17
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactor.java613
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactorException.java40
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableChannel.java72
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableDatagramChannel.java201
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableSocketChannel.java415
-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
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/package.rake120
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test.rake8
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test_pure.rake13
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.crt31
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.key51
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/dhparam.pem13
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/em_test_helper.rb154
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_attach.rb151
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_basic.rb321
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_channel.rb75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_completion.rb178
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_count.rb83
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_write.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_defer.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_deferrable.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_epoll.rb142
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_error_handler.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_exc.rb43
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_file_watch.rb85
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_fork.rb75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_futures.rb170
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_handler_check.rb35
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_hc.rb155
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient.rb238
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient2.rb128
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_idle_connection.rb31
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_inactivity_timeout.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv4.rb95
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv6.rb107
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_iterator.rb118
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_kb.rb28
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_line_protocol.rb33
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp.rb155
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp2.rb332
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_many_fds.rb22
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_next_tick.rb104
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_object_protocol.rb36
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pause.rb107
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pending_connect_timeout.rb52
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pool.rb196
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_process_watch.rb50
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_processes.rb128
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_proxy_connection.rb180
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pure.rb139
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_queue.rb64
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_resolver.rb105
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_running.rb14
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sasl.rb47
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_send_file.rb217
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_servers.rb33
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_shutdown_hooks.rb23
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpclient.rb75
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpserver.rb57
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sock_opt.rb54
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb293
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_args.rb78
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_dhparam.rb84
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_ecdh_curve.rb80
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_extensions.rb49
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_methods.rb65
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_protocols.rb246
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_verify.rb128
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_stomp.rb38
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_system.rb46
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_threaded_resource.rb63
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_tick_loop.rb59
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_timers.rb130
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ud.rb8
-rw-r--r--vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_unbind_reason.rb40
173 files changed, 31547 insertions, 0 deletions
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/CHANGELOG.md b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/CHANGELOG.md
new file mode 100644
index 0000000..cd42c39
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/CHANGELOG.md
@@ -0,0 +1,179 @@
+# Changelog
+
+## 1.2.7 (May 12, 2018)
+* Fix segfault on large numbers of connections [#843]
+
+## 1.2.6 (April 30, 2018)
+* *Fix segfault when an Exception is raised from unbind callback (for real this time!)*
+* Fix race condition while initializing the machine [#756]
+* Fix for newer compilers where bind() and std::bind() conflict [#830, #831]
+* Be verbose about SSL connection errors [#807]
+* Avoid explicitly calling class methods when in class scope
+* Java: Add EM_PROTO_SSL/TLS definitions [#773, #791]
+* Java: return zero when sending data to a closed connection [#475, #804]
+* Pure Ruby: Connection::error? calls report_connection_error_status [#801]
+
+## 1.2.5 (July 27, 2017)
+* Java: Use long for larger values in oneshot timer intervals [#784, #794]
+
+## 1.2.4 (July 27, 2017)
+* Java: Add EM_PROTO_SSL/TLS definitions [#773, #791]
+* Fix IPv6 UDP get_peername [#788]
+* Allow for larger values in oneshot timer intervals [#784, #793]
+* Update extconf.rb to allow MinGW builds with OpenSSL 1.1.0 [#785]
+
+## 1.2.3 (February 22, 2017)
+* Pure Ruby: Add get_sockname [#308, #772]
+* Fix segfault when an Exception is raised from unbind callback [#765, #766]
+* Allow destructors to throw when compiling in >= C++11 [#767]
+
+## 1.2.2 (January 23, 2017)
+* Java: Fix Fixnum deprecated warning in Ruby 2.4+ [#759]
+* Fix uncaught C++ exception in file watcher and raise InvalidSignature [#512, #757]
+* Fix connection count off-by-one for epoll and kqueue [#750]
+* Fix uninitialized variable warning in EM::P::HttpClient [#749]
+* Fix missing initial value for EventableDescriptor NextHeartbeat [#748]
+* Fix hostname resolution on Solaris, Ilumos, SmartOS, et al [#745, #746]
+* Improve reliability of tests, reduce public Internet accesses in tests [#656, #666, #749]
+
+## 1.2.1 (November 15, 2016)
+* Throw strerror(errno) when getsockname or getpeername fail [#683]
+* Use a single concrete implementation of getpeername/getsockname, the rest pure virtuals [#683]
+* Use gai_strerror to get the failure string from getaddrinfo [#744]
+* Fix deregistering descriptor when using KQUEUE [#728]
+* Fix to enable to work an example code in EM::Pool [#731]
+* LineText2: Add regular expression delimiter support [#706]
+* Pure Ruby: EM rescue ECONNREFUSED on initial TCP connect [#741]
+* Pure Ruby: EM SSL (working start_tls) [#712]
+* Pure Ruby: EM fixes [#707]
+* Java: Use Acceptors to get peer and sock names if not present in Connections [#743]
+
+## 1.2.0.1 (March 15, 2016)
+* Fix crash when accepting IPv6 connections due to struct sockaddr_in [#698, #699]
+
+## 1.2.0 (March 15, 2016)
+* Integrate work from the EventMachine-LE 1.1.x versions [#570]
+* Add start_tls options :ecdh_curve, :dhparam, :fail_if_no_peer_cert [#195, #275, #399, #665]
+* Add start_tls option :ssl_version for choosing SSL/TLS versions and ciphers [#359, #348, #603, #654]
+* Add start_tls option :sni_hostname to be passed to TLS params [#593]
+* Add method EM::Channel#num_subscribers to get the number of subscribers to a channel [#640]
+* Add support for proc-sources in EM::Iterator [#639]
+* Factor out method cleanup_machine to cleanup code from EM.run [#650]
+* Replace Exception class with StandardError [#637]
+* Close socket on close_connection even after close_connection_after_writing [#694]
+* Allow reusing of datagram socket/setting bind device [#662]
+* Handle deferred exceptions in reactor thread [#486]
+* Reimplement Queue to avoid shift/push performance problem [#311]
+* Windows: Switch from gethostbyname to getaddrinfo, support IPv6 addresses [#303, #630]
+* Windows: Use rake-compiler-dock to cross-compile gems [#627]
+* Windows: Add AppVeyor configuration for Windows CI testing [#578]
+* Windows: Bump rake-compiler to version 0.9.x [#542]
+* Fix compilation on AIX (w/ XLC) [#693]
+* Fix build on OpenBSD [#690]
+* Fix OpenSSL compile issue on AIX 7.1 [#678]
+* Fix EventMachine.fork_reactor keeps the threadpool of the original process [#425]
+* Fix to prevent event machine from stopping when a raise is done in an unbind [#327]
+
+## 1.0.9.1 (January 14, 2016)
+* Fix EPROTO not defined on Windows [#676]
+* Fix missing cast to struct sockaddr * [#671]
+* Fix bug in OpenSSL path detection [#675]
+
+## 1.0.9 (January 13, 2016)
+* Try more ways to detect OpenSSL [#602, #643, #661, #663, #668, #669]
+* Use WSAGetLastError in pipe.cpp same as ed.cpp [#659]
+* Test compiler flags with the C++ compiler and add them to CXXFLAGS [#634, #651]
+* Restore silent-fail on unsupported EM.epoll and EM.kqueue [#638, #649]
+* getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000 [#642, #648]
+* Add -Wno-address always-true because on Windows rb_fd_select [#578]
+* Remove the WITHOUT_SSL constant [#578]
+* Fix SSL error when the server replies a TLS Alert to our ClientHello [#544, #653]
+* Use WSAStringToAddress in lieu of inet_pton for IPv6 address detection on Windows [#595, #632]
+* Fix nasty TCP/IPv6 bug [#595, #632]
+* Use select_large_fdset on Solaris [#611, #625]
+* Detect the Solaris Studio compiler [#611, #625]
+* Throw a message with strerror included [#136, #621]
+
+## 1.0.8 (August 6, 2015)
+* fix kqueue assertion failed, postpone ArmKqueueWriter until all events are processed [#51, #176, #372, #401, #619]
+* fix Rubinius GC, crank the machine from Ruby space when running Rubinius [#201, #202, #617]
+* test to show that LineText2 preserves whitespace and newlines [#32, #622]
+* bump up compiler warnings and resolve them [#616]
+* fix Windows x64 use uintptr_t instead of unsigned long for binding pointers [#612, #615]
+* fix linetext2 unroll tail recursion to avoid stack level too deep [#609]
+* fix for compilation with SSL on windows [#601]
+* open file descriptors and sockets with O_CLOEXEC where possible [#298, #488, #591]
+* fix SmtpClient: send second EHLO after STARTTLS. [#589]
+* fix nul-terminated strings in C, use StringValueCStr instead of StringValuePtr
+
+## 1.0.7 (February 10, 2015)
+* fix delay in kqueue/epoll reactor shutdown when timers exist [#587]
+* fix memory leak introduced in v1.0.5 [#586]
+* expose EM.set_simultaneous_accept_count [#420]
+* fix busy loop when EM.run and EM.next_tick are invoked from exception handler [#452]
+
+## 1.0.6 (February 3, 2015)
+* add support for Rubinius Process::Status [#568]
+* small bugfixes for SmtpServer [#449]
+* update buftok.rb [#547]
+* fix assertion on Write() [#525]
+* work around mkmf.rb bug preventing gem installation [#574]
+* add pause/resume support to jruby reactor [#556]
+* fix pure ruby reactor to use 127.0.0.1 instead of localhost [#439]
+* fix compilation under macruby [#243]
+* add chunked encoding to http client [#111]
+* fix errors on win32 when dealing with pipes [1ea45498] [#105]
+
+## 1.0.5 (February 2, 2015)
+* use monotonic clocks on Linux, OS X, Solaris, and Windows [#563]
+* use the rb_fd_* API to get autosized fd_sets [#502]
+* add basic tests that the DNS resolver isn't leaking timers [#571]
+* update to test-unit 2.x and improve various unit tests [#551]
+* remove EventMachine_t::Popen code marked by ifdef OBSOLETE [#551]
+* ruby 2.0 may fail at Queue.pop, so rescue and complain to $stderr [#551]
+* set file handle to INVALID_HANDLE_VALUE after closing the file [#565]
+* use `defined?` instead of rescuing NameError for flow control [#535]
+* fix closing files and sockets on Windows [#564]
+* fix file uploads in Windows [#562]
+* catch failure to fork [#539]
+* use chunks for SSL write [#545]
+
+## 1.0.4 (December 19, 2014)
+* add starttls_options to smtp server [#552]
+* fix closesocket on windows [#497]
+* fix build on ruby 2.2 [#503]
+* fix build error on ruby 1.9 [#508]
+* fix timer leak during dns resolution [#489]
+* add concurrency validation to EM::Iterator [#468]
+* add get_file_descriptor to get fd for a signature [#467]
+* add EM.attach_server and EM.attach_socket_server [#465, #466]
+* calling pause from receive_data takes effect immediately [#464]
+* reactor_running? returns false after fork [#455]
+* fix infinite loop on double close [edc4d0e6, #441, #445]
+* fix compilation issue on llvm [#433]
+* fix socket error codes on win32 [ff811a81]
+* fix EM.stop latency when timers exist [8b613d05, #426]
+* fix infinite loop when system time changes [1427a2c80, #428]
+* fix crash when callin attach/detach in the same tick [#427]
+* fix compilation issue on solaris [#416]
+
+## 1.0.3 (March 8, 2013)
+* EM.system was broken in 1.0.2 release [#413]
+
+## 1.0.2 (March 8, 2013)
+* binary win32 gems now include fastfilereader shim [#222]
+* fix long-standing connection timeout issues [27fdd5b, igrigorik/em-http-request#222]
+* http and line protocol cleanups [#193, #151]
+* reactor return value cleanup [#225]
+* fix double require from gemspec [#284]
+* fix smtp server reset behavior [#351]
+* fix EM.system argument handling [#322]
+* ruby 1.9 compat in smtp server and stomp protocols [#349, #315]
+* fix pause from post_init [#380]
+
+## 1.0.1 (February 27, 2013)
+* use rb_wait_for_single_fd() on ruby 2.0 to fix rb_thread_select() deprecation [#363]
+* fix epoll/kqueue mode in ruby 2.0 by removing calls to rb_enable_interrupt() [#248, #389]
+* fix memory leak when verifying ssl cerificates [#403]
+* fix initial connection delay [#393, #374]
+* fix build on windows [#371]
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/GNU b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/GNU
new file mode 100644
index 0000000..3b70c5b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/GNU
@@ -0,0 +1,281 @@
+.
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+ freedom to share and change it. By contrast, the GNU General Public
+ License is intended to guarantee your freedom to share and change free
+ software--to make sure the software is free for all its users. This
+ General Public License applies to most of the Free Software
+ Foundation's software and to any other program whose authors commit to
+ using it. (Some other Free Software Foundation software is covered by
+ the GNU Lesser General Public License instead.) You can apply it to
+ your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+ price. Our General Public Licenses are designed to make sure that you
+ have the freedom to distribute copies of free software (and charge for
+ this service if you wish), that you receive source code or can get it
+ if you want it, that you can change the software or use pieces of it
+ in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+ anyone to deny you these rights or to ask you to surrender the rights.
+ These restrictions translate to certain responsibilities for you if you
+ distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+ gratis or for a fee, you must give the recipients all the rights that
+ you have. You must make sure that they, too, receive or can get the
+ source code. And you must show them these terms so they know their
+ rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+ (2) offer you this license which gives you legal permission to copy,
+ distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+ that everyone understands that there is no warranty for this free
+ software. If the software is modified by someone else and passed on, we
+ want its recipients to know that what they have is not the original, so
+ that any problems introduced by others will not reflect on the original
+ authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+ patents. We wish to avoid the danger that redistributors of a free
+ program will individually obtain patent licenses, in effect making the
+ program proprietary. To prevent this, we have made it clear that any
+ patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+ modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+ a notice placed by the copyright holder saying it may be distributed
+ under the terms of this General Public License. The "Program", below,
+ refers to any such program or work, and a "work based on the Program"
+ means either the Program or any derivative work under copyright law:
+ that is to say, a work containing the Program or a portion of it,
+ either verbatim or with modifications and/or translated into another
+ language. (Hereinafter, translation is included without limitation in
+ the term "modification".) Each licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of
+ running the Program is not restricted, and the output from the Program
+ is covered only if its contents constitute a work based on the
+ Program (independent of having been made by running the Program).
+ Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an appropriate
+ copyright notice and disclaimer of warranty; keep intact all the
+ notices that refer to this License and to the absence of any warranty;
+ and give any other recipients of the Program a copy of this License
+ along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and
+ you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+ of it, thus forming a work based on the Program, and copy and
+ distribute such modifications or work under the terms of Section 1
+ above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the Program,
+ and can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works. But when you
+ distribute the same sections as part of a whole which is a work based
+ on the Program, the distribution of the whole must be on the terms of
+ this License, whose permissions for other licensees extend to the
+ entire whole, and thus to each and every part regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program
+ with the Program (or with a work based on the Program) on a volume of
+ a storage or distribution medium does not bring the other work under
+ the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+ under Section 2) in object code or executable form under the terms of
+ Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+ The source code for a work means the preferred form of the work for
+ making modifications to it. For an executable work, complete source
+ code means all the source code for all modules it contains, plus any
+ associated interface definition files, plus the scripts used to
+ control compilation and installation of the executable. However, as a
+ special exception, the source code distributed need not include
+ anything that is normally distributed (in either source or binary
+ form) with the major components (compiler, kernel, and so on) of the
+ operating system on which the executable runs, unless that component
+ itself accompanies the executable.
+
+ If distribution of executable or object code is made by offering
+ access to copy from a designated place, then offering equivalent
+ access to copy the source code from the same place counts as
+ distribution of the source code, even though third parties are not
+ compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+ except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense or distribute the Program is
+ void, and will automatically terminate your rights under this License.
+ However, parties who have received copies, or rights, from you under
+ this License will not have their licenses terminated so long as such
+ parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+ signed it. However, nothing else grants you permission to modify or
+ distribute the Program or its derivative works. These actions are
+ prohibited by law if you do not accept this License. Therefore, by
+ modifying or distributing the Program (or any work based on the
+ Program), you indicate your acceptance of this License to do so, and
+ all its terms and conditions for copying, distributing or modifying
+ the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the
+ original licensor to copy, distribute or modify the Program subject to
+ these terms and conditions. You may not impose any further
+ restrictions on the recipients' exercise of the rights granted herein.
+ You are not responsible for enforcing compliance by third parties to
+ this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot
+ distribute so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you
+ may not distribute the Program at all. For example, if a patent
+ license would not permit royalty-free redistribution of the Program by
+ all those who receive copies directly or indirectly through you, then
+ the only way you could satisfy both it and this License would be to
+ refrain entirely from distribution of the Program.
+
+ If any portion of this section is held invalid or unenforceable under
+ any particular circumstance, the balance of the section is intended to
+ apply and the section as a whole is intended to apply in other
+ circumstances.
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system, which is
+ implemented by public license practices. Many people have made
+ generous contributions to the wide range of software distributed
+ through that system in reliance on consistent application of that
+ system; it is up to the author/donor to decide if he or she is willing
+ to distribute software through any other system and a licensee cannot
+ impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to
+ be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+ certain countries either by patents or by copyrighted interfaces, the
+ original copyright holder who places the Program under this License
+ may add an explicit geographical distribution limitation excluding
+ those countries, so that distribution is permitted only in or among
+ countries not thus excluded. In such case, this License incorporates
+ the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+ of the General Public License from time to time. Such new versions will
+ be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation. If the Program does not specify a version number of
+ this License, you may choose any version ever published by the Free Software
+ Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+ programs whose distribution conditions are different, write to the author
+ to ask for permission. For software which is copyrighted by the Free
+ Software Foundation, write to the Free Software Foundation; we sometimes
+ make exceptions for this. Our decision will be guided by the two goals
+ of preserving the free status of all derivatives of our free software and
+ of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+ REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/LICENSE b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/LICENSE
new file mode 100644
index 0000000..fbe8c83
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/LICENSE
@@ -0,0 +1,60 @@
+EventMachine is copyrighted free software owned by Francis Cianfrocca
+(blackhedd ... gmail.com). The Owner of this software permits you to
+redistribute and/or modify the software under either the terms of the GPL
+version 2 (see the file GPL), or the conditions below ("Ruby License"):
+
+ 1. You may make and give away verbatim copies of the source form of this
+ software without restriction, provided that you retain ALL of the
+ original copyright notices and associated disclaimers.
+
+ 2. You may modify your copy of the software in any way, provided that
+ you do at least ONE of the following:
+
+ a) place your modifications in the Public Domain or otherwise
+ make them Freely Available, such as by posting said
+ modifications to Usenet or an equivalent medium, or by allowing
+ the author to include your modifications in the software.
+
+ b) use the modified software only within your corporation or
+ organization.
+
+ c) give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d) make other distribution arrangements with the Owner.
+
+ 3. You may distribute the software in object code or binary form,
+ provided that you do at least ONE of the following:
+
+ a) distribute the binaries and library files of the software,
+ together with instructions (in a manual page or equivalent)
+ on where to get the original distribution.
+
+ b) accompany the distribution with the machine-readable source of
+ the software.
+
+ c) give non-standard binaries non-standard names, with
+ instructions on where to get the original software distribution.
+
+ d) make other distribution arrangements with the Owner.
+
+ 4. You may modify and include parts of the software into any other
+ software (possibly commercial), provided you comply with the terms in
+ Sections 1, 2, and 3 above. But some files in the distribution
+ are not written by the Owner, so they may be made available to you
+ under different terms.
+
+ For the list of those files and their copying conditions, see the
+ file LEGAL.
+
+ 5. The scripts and library files supplied as input to or produced as
+ output from the software do not automatically fall under the
+ copyright of the software, but belong to whoever generated them,
+ and may be sold commercially, and may be aggregated with this
+ software.
+
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/README.md b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/README.md
new file mode 100644
index 0000000..4b17cb4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/README.md
@@ -0,0 +1,110 @@
+# About EventMachine [![Build Status](https://travis-ci.org/eventmachine/eventmachine.svg?branch=master)](https://travis-ci.org/eventmachine/eventmachine) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability)
+
+
+## What is EventMachine ##
+
+EventMachine is an event-driven I/O and lightweight concurrency library for Ruby.
+It provides event-driven I/O using the [Reactor pattern](http://en.wikipedia.org/wiki/Reactor_pattern),
+much like [JBoss Netty](http://www.jboss.org/netty), [Apache MINA](http://mina.apache.org/),
+Python's [Twisted](http://twistedmatrix.com), [Node.js](http://nodejs.org), libevent and libev.
+
+EventMachine is designed to simultaneously meet two key needs:
+
+ * Extremely high scalability, performance and stability for the most demanding production environments.
+ * An API that eliminates the complexities of high-performance threaded network programming,
+ allowing engineers to concentrate on their application logic.
+
+This unique combination makes EventMachine a premier choice for designers of critical networked
+applications, including Web servers and proxies, email and IM production systems, authentication/authorization
+processors, and many more.
+
+EventMachine has been around since the early 2000s and is a mature and battle-tested library.
+
+
+## What EventMachine is good for? ##
+
+ * Scalable event-driven servers. Examples: [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/).
+ * Scalable asynchronous clients for various protocols, RESTful APIs and so on. Examples: [em-http-request](https://github.com/igrigorik/em-http-request) or [amqp gem](https://github.com/ruby-amqp/amqp).
+ * Efficient network proxies with custom logic. Examples: [Proxymachine](https://github.com/mojombo/proxymachine/).
+ * File and network monitoring tools. Examples: [eventmachine-tail](https://github.com/jordansissel/eventmachine-tail) and [logstash](https://github.com/logstash/logstash).
+
+
+
+## What platforms are supported by EventMachine? ##
+
+EventMachine supports Ruby 1.8.7 through 2.6, REE, JRuby and **works well on Windows** as well
+as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors).
+
+
+
+## Install the gem ##
+
+Install it with [RubyGems](https://rubygems.org/)
+
+ gem install eventmachine
+
+or add this to your Gemfile if you use [Bundler](http://gembundler.com/):
+
+ gem "eventmachine"
+
+
+
+## Getting started ##
+
+For an introduction to EventMachine, check out:
+
+ * [blog post about EventMachine by Ilya Grigorik](http://www.igvita.com/2008/05/27/ruby-eventmachine-the-speed-demon/).
+ * [EventMachine Introductions by Dan Sinclair](http://everburning.com/news/eventmachine-introductions.html).
+
+
+### Server example: Echo server ###
+
+Here's a fully-functional echo server written with EventMachine:
+
+```ruby
+ require 'eventmachine'
+
+ module EchoServer
+ def post_init
+ puts "-- someone connected to the echo server!"
+ end
+
+ def receive_data data
+ send_data ">>>you sent: #{data}"
+ close_connection if data =~ /quit/i
+ end
+
+ def unbind
+ puts "-- someone disconnected from the echo server!"
+ end
+end
+
+# Note that this will block current thread.
+EventMachine.run {
+ EventMachine.start_server "127.0.0.1", 8081, EchoServer
+}
+```
+
+
+## EventMachine documentation ##
+
+Currently we only have [reference documentation](http://rdoc.info/github/eventmachine/eventmachine/frames) and a [wiki](https://github.com/eventmachine/eventmachine/wiki).
+
+
+## Community and where to get help ##
+
+ * Join the [mailing list](http://groups.google.com/group/eventmachine) (Google Group)
+ * Join IRC channel #eventmachine on irc.freenode.net
+
+
+## License and copyright ##
+
+EventMachine is copyrighted free software made available under the terms
+of either the GPL or Ruby's License.
+
+Copyright: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
+
+
+## Alternatives ##
+
+If you are unhappy with EventMachine and want to use Ruby, check out [Celluloid](https://celluloid.io/).
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/DocumentationGuidesIndex.md b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/DocumentationGuidesIndex.md
new file mode 100644
index 0000000..b8ce5a2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/DocumentationGuidesIndex.md
@@ -0,0 +1,27 @@
+# EventMachine documentation guides #
+
+Welcome to the documentation guides for [EventMachine](http://github.com/eventmachine/eventmachine),
+a fast and simple event-processing library for Ruby programs (à la JBoss Netty, Twisted, Node.js
+and so on).
+
+## Guide list ##
+
+ * {file:docs/GettingStarted.md Getting started with EventMachine}
+ * {file:docs/EventDrivenServers.md Writing event-driven servers}
+ * {file:docs/EventDrivenClients.md Writing event-driven clients}
+ * {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery}
+ * {file:docs/TLS.md TLS (aka SSL)}
+ * {file:docs/Ecosystem.md EventMachine ecosystem}: Thin, Goliath, em-http-request, em-websockets, Proxymachine and beyond
+ * {file:docs/BlockingEventLoop.md On blocking the event loop: why it is harmful for performance and how to avoid it}
+ * {file:docs/LightweightConcurrency.md Lightweight concurrency with EventMachine}
+ * {file:docs/Deferrables.md Deferrables}
+ * {file:docs/ModernKernelInputOutputAPIs.md Brief introduction to epoll, kqueue, select}
+ * {file:docs/WorkingWithOtherIOSources.md Working with other IO sources such as the keyboard}
+
+
+## Tell us what you think! ##
+
+Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
+or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
+Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
+key to making documentation better.
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/GettingStarted.md b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/GettingStarted.md
new file mode 100644
index 0000000..63acbb7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/GettingStarted.md
@@ -0,0 +1,521 @@
+# @title Getting Started with Ruby EventMachine
+# @markup markdown
+# @author Michael S. Klishin, Dan Sinclair
+
+# Getting started with Ruby EventMachine #
+
+
+## About this guide ##
+
+This guide is a quick tutorial that helps you to get started with EventMachine for writing event-driven
+servers, clients and using it as a lightweight concurrency library.
+It should take about 20 minutes to read and study the provided code examples. This guide covers
+
+ * Installing EventMachine via [Rubygems](http://rubygems.org) and [Bundler](http://gembundler.com).
+ * Building an Echo server, the "Hello, world"-like code example of network servers.
+ * Building a simple chat, both server and client.
+ * Building a very small asynchronous Websockets client.
+
+
+## Covered versions ##
+
+This guide covers EventMachine v0.12.10 and 1.0 (including betas).
+
+
+## Level ##
+
+This guide assumes you are comfortable (but not necessary a guru) with the command line. On Microsoft Windows™,
+we recommend you to use [JRuby](http://jruby.org) when running these examples.
+
+
+## Installing EventMachine ##
+
+### Make sure you have Ruby installed ###
+
+This guide assumes you have one of the supported Ruby implementations installed:
+
+ * Ruby 1.8.7
+ * Ruby 1.9.2
+ * [JRuby](http://jruby.org) (we recommend 1.6)
+ * [Rubinius](http://rubini.us) 1.2 or higher
+ * [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com)
+
+EventMachine works on Microsoft Windows™.
+
+
+### With Rubygems ###
+
+To install the EventMachine gem do
+
+ gem install eventmachine
+
+
+### With Bundler ###
+
+ gem "eventmachine"
+
+
+### Verifying your installation ###
+
+Lets verify your installation with this quick IRB session:
+
+ irb -rubygems
+
+ ruby-1.9.2-p180 :001 > require "eventmachine"
+ => true
+ ruby-1.9.2-p180 :002 > EventMachine::VERSION
+ => "1.0.0.beta.3"
+
+
+## An Echo Server Example ##
+
+Lets begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the
+same data that was provided. First, here's the code:
+
+{include:file:examples/guides/getting\_started/01\_eventmachine\_echo_server.rb}
+
+
+When run, the server binds to port 10000. We can connect using Telnet and verify it's working:
+
+ telnet localhost 10000
+
+On my machine the output looks like:
+
+ ~ telnet localhost 10000
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+
+Let's send something to our server. Type in "Hello, EventMachine" and hit Enter. The server will respond with
+the same string:
+
+ ~ telnet localhost 10000
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ Hello, EventMachine
+ # (here we hit Enter)
+ Hello, EventMachine
+ # (this ^^^ is our echo server reply)
+
+It works! Congratulations, you now can tell your Node.js-loving friends that you "have done some event-driven programming, too".
+Oh, and to stop Telnet, hit Control + Shift + ] and then Control + C.
+
+Lets walk this example line by line and see what's going on. These lines
+
+ require 'rubygems' # or use Bundler.setup
+ require 'eventmachine'
+
+probably look familiar: you use [RubyGems](http://rubygems.org) (or [Bundler](http://gembundler.com/)) for dependencies and then require EventMachine gem. Boring.
+
+Next:
+
+ class EchoServer < EventMachine::Connection
+ def receive_data(data)
+ send_data(data)
+ end
+ end
+
+Is the implementation of our echo server. We define a class that inherits from {EventMachine::Connection}
+and a handler (aka callback) for one event: when we receive data from a client.
+
+EventMachine handles the connection setup, receiving data and passing it to our handler, {EventMachine::Connection#receive_data}.
+
+Then we implement our protocol logic, which in the case of Echo is pretty trivial: we send back whatever we receive.
+To do so, we're using {EventMachine::Connection#send_data}.
+
+Lets modify the example to recognize `exit` command:
+
+{include:file:examples/guides/getting\_started/02\_eventmachine\_echo_server\_that\_recognizes\_exit\_command.rb}
+
+Our `receive\_data` changed slightly and now looks like this:
+
+ def receive_data(data)
+ if data.strip =~ /exit$/i
+ EventMachine.stop_event_loop
+ else
+ send_data(data)
+ end
+ end
+
+Because incoming data has trailing newline character, we strip it off before matching it against a simple regular
+expression. If the data ends in `exit`, we stop EventMachine event loop with {EventMachine.stop_event_loop}. This unblocks
+main thread and it finishes execution, and our little program exits as the result.
+
+To summarize this first example:
+
+ * Subclass {EventMachine::Connection} and override {EventMachine::Connection#send_data} to handle incoming data.
+ * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}.
+ * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop})
+
+Lets move on to a slightly more sophisticated example that will introduce several more features and methods
+EventMachine has to offer.
+
+
+## A Simple Chat Server Example ##
+
+Next we will write a simple chat. Initially clients will still use telnet to connect, but then we will add little
+client application that will serve as a proxy between telnet and the chat server. This example is certainly longer
+(~ 150 lines with whitespace and comments) so instead of looking at the final version and going through it line by line,
+we will instead begin with a very simple version that only keeps track of connected clients and then add features
+as we go.
+
+To set some expectations about our example:
+
+ * It will keep track of connected clients
+ * It will support a couple of commands, à la IRC
+ * It will support direct messages using Twitter-like @usernames
+ * It won't use MongoDB, fibers or distributed map/reduce for anything but will be totally [Web Scale™](http://bit.ly/webscaletm) nonetheless. Maybe even [ROFLscale](http://bit.ly/roflscalevideo).
+
+### Step one: detecting connections and disconnectons ###
+
+First step looks like this:
+
+{include:file:examples/guides/getting\_started/04\_simple\_chat\_server\_step\_one.rb}
+
+We see familiar {EventMachine.run} and {EventMachine.start_server}, but also {EventMachine::Connection#post_init} and {EventMachine::Connection#unbind} we haven't
+met yet. We don't use them in this code, so when are they run? Like {EventMachine::Connection#receive_data}, these methods are callbacks. EventMachine calls them
+when certain events happen:
+
+ * {EventMachine#post_init} is called by the event loop immediately after the network connection has been established.
+ In the chat server example case, this is when a new client connects.
+ * {EventMachine#unbind} is called when client disconnects, connection is closed or is lost (because of a network issue, for example).
+
+All our chat server does so far is logging connections or disconnections. What we want it to do next is to keep track of connected clients.
+
+
+### Step two: keep track of connected clients ###
+
+Next iteration of the code looks like this:
+
+{include:file:examples/guides/getting\_started/05\_simple\_chat\_server\_step\_two.rb}
+
+While the code we added is very straightforward, we have to clarify one this first: subclasses of {EventMachine::Connection} are instantiated by
+EventMachine for every new connected peer. So for 10 connected chat clients, there will be 10 separate `SimpleChatServer` instances in our
+server process. Like any other objects, they can be stored in a collection, can provide public API other objects use, can instantiate or inject
+dependencies and in general live a happy life all Ruby objects live until garbage collection happens.
+
+In the example above we use a @@class_variable to keep track of connected clients. In Ruby, @@class variables are accessible from instance
+methods so we can add new connections to the list from `SimpleChatServer#post_init` and remove them in `SimpleChatServer#unbind`. We can also
+filter connections by some criteria, as `SimpleChatServer#other_peers demonstrates`.
+
+So, we keep track of connections but how do we identify them? For a chat app, it's pretty common to use usernames for that. Lets ask our clients
+to enter usernames when they connect.
+
+
+### Step three: adding usernames ##
+
+To add usernames, we need to add a few things:
+
+ * We need to invite newly connected clients to enter their username.
+ * A reader (getter) method on our {EventMachine::Connection} subclass.
+ * An idea of connection state (keeping track of whether a particular participant had entered username before).
+
+Here is one way to do it:
+
+{include:file:examples/guides/getting\_started/06\_simple\_chat\_server\_step\_three.rb}
+
+This is quite an update so lets take a look at each method individually. First, `SimpleChatServer#post_init`:
+
+ def post_init
+ @username = nil
+ puts "A client has connected..."
+ ask_username
+ end
+
+To keep track of username we ask chat participants for, we add @username instance variable to our connection class. Connection
+instances are just Ruby objects associated with a particular connected peer, so using @ivars is very natural. To make username
+value accessible to other objects, we added a reader method that was not shown on the snippet above.
+
+Lets dig into `SimpleChatServer#ask_username`:
+
+ def ask_username
+ self.send_line("[info] Enter your username:")
+ end # ask_username
+
+ # ...
+
+ def send_line(line)
+ self.send_data("#{line}\n")
+ end # send_line(line)
+
+Nothing new here, we are using {EventMachine::Connection#send_data} which we have seen before.
+
+
+In `SimpleChatServer#receive_data` we now have to check if the username was entered or we need
+to ask for it:
+
+ def receive_data(data)
+ if entered_username?
+ handle_chat_message(data.strip)
+ else
+ handle_username(data.strip)
+ end
+ end
+
+ # ...
+
+ def entered_username?
+ !@username.nil? && !@username.empty?
+ end # entered_username?
+
+Finally, handler of chat messages is not yet implemented:
+
+ def handle_chat_message(msg)
+ raise NotImplementedError
+ end
+
+Lets try this example out using Telnet:
+
+ ~ telnet localhost 10000
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ [info] Enter your username:
+ antares_
+ [info] Ohai, antares_
+
+and the server output:
+
+ A client has connected...
+ antares_ has joined
+
+This version requires you to remember how to terminate your Telnet session (Ctrl + Shift + ], then Ctrl + C).
+It is annoying, so why don't we add the same `exit` command to our chat server?
+
+
+### Step four: adding exit command and delivering chat messages ####
+
+{include:file:examples/guides/getting\_started/07\_simple\_chat\_server\_step\_four.rb}
+
+TBD
+
+Lets test-drive this version. Client A:
+
+ ~ telnet localhost 10000
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ [info] Enter your username:
+ michael
+ [info] Ohai, michael
+ Hi everyone
+ michael: Hi everyone
+ joe has joined the room
+ # here ^^^ client B connects, lets greet him
+ hi joe
+ michael: hi joe
+ joe: hey michael
+ # ^^^ client B replies
+ exit
+ # ^^^ out command in action
+ Connection closed by foreign host.
+
+Client B:
+
+ ~ telnet localhost 10000
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ [info] Enter your username:
+ joe
+ [info] Ohai, joe
+ michael: hi joe
+ # ^^^ client A greets us, lets reply
+ hey michael
+ joe: hey michael
+ exit
+ # ^^^ out command in action
+ Connection closed by foreign host.
+
+And finally, the server output:
+
+ A client has connected...
+ michael has joined
+ A client has connected...
+ _antares has joined
+ [info] _antares has left
+ [info] michael has left
+
+Our little char server now supports usernames, sending messages and the `exit` command. Next up, private (aka direct) messages.
+
+
+### Step five: adding direct messages and one more command ###
+
+To add direct messages, we come up with a simple convention: private messages begin with @username and may have optional colon before
+message text, like this:
+
+ @joe: hey, how do you like eventmachine?
+
+This convention makes parsing of messages simple so that we can concentrate on delivering them to a particular client connection.
+Remember when we added `username` reader on our connection class? That tiny change makes this step possible: when a new direct
+message comes in, we extract username and message text and then find then connection for @username in question:
+
+ #
+ # Message handling
+ #
+
+ def handle_chat_message(msg)
+ if command?(msg)
+ self.handle_command(msg)
+ else
+ if direct_message?(msg)
+ self.handle_direct_message(msg)
+ else
+ self.announce(msg, "#{@username}:")
+ end
+ end
+ end # handle_chat_message(msg)
+
+ def direct_message?(input)
+ input =~ DM_REGEXP
+ end # direct_message?(input)
+
+ def handle_direct_message(input)
+ username, message = parse_direct_message(input)
+
+ if connection = @@connected_clients.find { |c| c.username == username }
+ puts "[dm] @#{@username} => @#{username}"
+ connection.send_line("[dm] @#{@username}: #{message}")
+ else
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
+ end
+ end # handle_direct_message(input)
+
+ def parse_direct_message(input)
+ return [$1, $2] if input =~ DM_REGEXP
+ end # parse_direct_message(input)
+
+This snippet demonstrates how one connection instance can obtain another connection instance and send data to it.
+This is a very powerful feature, consider just a few use cases:
+
+ * Peer-to-peer protocols
+ * Content-aware routing
+ * Efficient streaming with optional filtering
+
+Less common use cases include extending C++ core of EventMachine to provide access to hardware that streams events that
+can be re-broadcasted to any interested parties connected via TCP, UDP or something like AMQP or WebSockets. With this,
+sky is the limit. Actually, EventMachine has several features for efficient proxying data between connections.
+We will not cover them in this guide.
+
+One last feature that we are going to add to our chat server is the `status` command that tells you current server time and how many people
+are there in the chat room:
+
+ #
+ # Commands handling
+ #
+
+ def command?(input)
+ input =~ /(exit|status)$/i
+ end # command?(input)
+
+ def handle_command(cmd)
+ case cmd
+ when /exit$/i then self.close_connection
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
+ end
+ end # handle_command(cmd)
+
+Hopefully this piece of code is easy to follow. Try adding a few more commands, for example, the `whoishere` command that lists people
+currently in the chat room.
+
+In the end, our chat server looks like this:
+
+{include:file:examples/guides/getting\_started/08\_simple\_chat\_server\_step\_five.rb}
+
+We are almost done with the server but there are some closing thoughts.
+
+
+### Step six: final version ###
+
+Just in case, here is the final version of the chat server code we have built:
+
+{include:file:examples/guides/getting\_started/03\_simple\_chat\_server.rb}
+
+
+### Step seven: future directions and some closing thoughts ###
+
+The chat server is just about 150 lines of Ruby including empty lines and comments, yet it has a few features most of chat server
+examples never add. We did not, however, implement many other features that popular IRC clients like [Colloquy](http://colloquy.info) have:
+
+ * Chat moderation
+ * Multiple rooms
+ * Connection timeout detection
+
+How would one go about implementing them? We thought it is worth discussing what else EventMachine has to offer and what ecosystem projects
+one can use to build a really feature-rich Web-based IRC chat client.
+
+With multiple rooms it's more or less straightforward, just add one more hash and a bunch of commands and use the information about which rooms participant
+is in when you are delivering messages. There is nothing in EventMachine itself that can make the job much easier for developer.
+
+To implement chat moderation feature you may want to do a few things:
+
+ * Work with client IP addresses. Maybe we want to consider everyone who connects from certain IPs a moderator.
+ * Access persistent data about usernames of moderators and their credentials.
+
+Does EventMachine have anything to offer here? It does. To obtain peer IP address, take a look at {EventMachine::Connection#get_peername}. The name of this method is
+a little bit misleading and originates from low-level socket programming APIs.
+
+#### A whirlwind tour of the EventMachine ecosystem ####
+
+To work with data stores you can use several database drivers that ship with EventMachine itself, however, quite often there are some 3rd party projects in
+the EventMachine ecosystem that have more features, are faster or just better maintained. So we figured it will be helpful to provide a few pointers
+to some of those projects:
+
+ * For MySQL, check out [em-mysql](https://github.com/eventmachine/em-mysql) project.
+ * For PostgreSQL, have a look at Mike Perham's [EventMachine-based PostgreSQL driver](https://github.com/mperham/em_postgresql).
+ * For Redis, there is a young but already popular [em-hiredis](https://github.com/mloughran/em-hiredis) library that combines EventMachine's non-blocking I/O with
+ extreme performance of the official Redis C client, [hiredis](https://github.com/antirez/hiredis).
+ * For MongoDB, see [em-mongo](https://github.com/bcg/em-mongo)
+ * For Cassandra, Mike Perham [added transport agnosticism feature](http://www.mikeperham.com/2010/02/09/cassandra-and-eventmachine/) to the [cassandra gem](https://rubygems.org/gems/cassandra).
+
+[Riak](http://www.basho.com/products_riak_overview.php) and CouchDB talk HTTP so it's possible to use [em-http-request](https://github.com/igrigorik/em-http-request).
+If you are aware of EventMachine-based non-blocking drivers for these databases, as well as for HBase, let us know on the [EventMachine mailing list](http://groups.google.com/group/eventmachine).
+Also, EventMachine supports TLS (aka SSL) and works well on [JRuby](http://jruby.org) and Windows.
+
+Learn more in our {file:docs/Ecosystem.md EventMachine ecosystem} and {file:docs/TLS.md TLS (aka SSL)} guides.
+
+
+#### Connection loss detection ####
+
+Finally, connection loss detection. When our chat participant closes her laptop lid, how do we know that she is no longer active? The answer is, when EventMachine
+detects TCP connectin closure, it calls {EventMachine::Connection#unbind}. Version 1.0.beta3 and later also pass an optional argument to that method. The argument
+indicates what error (if any) caused the connection to be closed.
+
+Learn more in our {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery} guide.
+
+
+#### What the Chat Server Example doesn't demonstrate ####
+
+This chat server also leaves out something production quality clients and servers must take care of: buffering. We intentionally did not include any buffering in
+our chat server example: it would only distract you from learning what you really came here to learn: how to use EventMachine to build blazing fast asynchronous
+networking programs quickly. However, {EventMachine::Connection#receive_data} does not offer any guarantees that you will be receiving "whole messages" all the time,
+largely because the underlying transport (UDP or TCP) does not offer such guarantees. Many protocols, for example, AMQP, mandate that large content chunks are
+split into smaller _frames_ of certain size. This means that [amq-client](https://github.com/ruby-amqp/amq-client) library, for instance, that has EventMachine-based driver,
+has to deal with figuring out when exactly we received "the whole message". To do so, it uses buffering and employs various checks to detect _frame boundaries_.
+So **don't be deceived by the simplicity of this chat example**: it intentionally leaves framing out, but real world protocols usually require it.
+
+
+
+## A (Proxying) Chat Client Example ##
+
+TBD
+
+
+## Wrapping up ##
+
+This tutorial ends here. Congratulations! You have learned quite a bit about EventMachine.
+
+
+## What to read next ##
+
+The documentation is organized as a {file:docs/DocumentationGuidesIndex.md number of guides}, covering all kinds of
+topics. TBD
+
+
+## Tell us what you think! ##
+
+Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3)
+or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered?
+Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is
+key to making documentation better.
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/ChangeLog b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/ChangeLog
new file mode 100644
index 0000000..c7a6c48
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/ChangeLog
@@ -0,0 +1,211 @@
+01Oct06: Replaced EventMachine#open_datagram_server with a version that can
+ take a Class or a Module, instead of just a Module. Thanks to Tobias
+ Gustafsson for pointing out the missing case.
+04Oct06: Supported subsecond timer resolutions, per request by Jason Roelofs.
+05Oct06: Added EventMachine#set_quantum, which sets the timer resolution.
+15Nov06: Added Connection#set_comm_inactivity_timeout.
+15Nov06: Checked in a Line-and-Text Protocol Handler.
+18Nov06: Checked in a Header-and-Body Protocol Handler.
+22Nov06: Changed EventMachine#reconnect: no longer excepts when called on an
+ already-connected handler.
+28Nov06: Supported a binary-unix gem.
+19Dec06: Added EventMachine#set_effective_user.
+05Jan07: Upped max outstanding timers to 1000.
+15May07: Applied Solaris patches from Brett Eisenberg
+22May07: Cleaned up the license text in all the source files.
+22May07: Released version 0.7.2
+
+23May07: Per suggestion from Bill Kelly, fixed a bug with the initialization
+ of the network libraries under Windows. The goal is to enable EM to
+ be used without Ruby.
+28May07: Applied patch from Bill Kelly, refactors the declarations of
+ event names to make EM easier to use from C programs without Ruby.
+31May07: Added a preliminary implementation of EventMachine#popen.
+01Jun07: Added EM, a "pseudo-alias" for EventMachine.
+01Jun07: Added EM#next_tick.
+01Jun07: Added EM::Connection#get_outbound_data_size
+05Jun07: Removed the code which loads a pure-Ruby EM library in case the
+ compiled extension is unavailable. Suggested by Moshe Litvin.
+06Jun07: Preliminary epoll implementation.
+12Jun07: Added an evented popen implementation that, like Ruby's, is
+ full-duplex and makes the subprocess PID available to the caller.
+06Jul07: Performance-tweaked the callback dispatcher in eventmachine.rb.
+10Jul07: Released version 0.8.0.
+12Jul07: Applied patches from Tim Pease to fix Solaris build problems.
+15Jul07: Created a new provisional source branch, experiments/jruby-1.
+ This is a preliminary implementation of the EM reactor in Java,
+ suitable for use with JRuby.
+17Jul07: Added EventMachine#stop_server, per request from Kirk Haines,
+ and associated unit tests.
+22Jul07: Added EventMachine#stream_file_data. This is a very fast and scalable
+ way of sending data from static files over network connections. It
+ has separate implementations for small files and large file, and
+ has tunings to minimize memory consumption.
+26Jul07: Added some patches by Kirk Haines to improve the behavior of
+ EM::Connection#send_file_data_to_connection.
+26Jul07: Added a C++ module for directly integrating EM into C++ programs
+ with no Ruby dependencies. Needs example code.
+29Jul07: Added EventMachine::Protocols::LineText2.
+29Jul07: Added EventMachine::Protocols::Stomp.
+30Jul07: Added sys/stat.h to project.h to fix compilation bug on Darwin.
+13Aug07: Added EventMachine#reactor_running?
+15Aug07: Added parameters for EventMachine::Connection:start_tls that can be
+ used to specify client-side private keys and certificates.
+17Aug07: Added EventMachine#run_block, a sugaring for a common use case.
+24Aug07: Added a preliminary keyboard handler. Needs docs and testing on
+ windows.
+26Aug07: Created EventMachine::Spawnable, an implementation of Erlang-like
+ processes.
+27Aug07: Silenced some -w warnings, requested by James Edward Gray II.
+30Aug07: Added cookies to EM::HttpClient#request.
+04Sep07: Added an initial implementation of an evented SMTP client.
+04Sep07: Added an initial implementation of an evented SMTP server.
+10Sep07: Changed EM#spawn to run spawned blocks in the context of the
+ SpawnedProcess object, not of whatever was the active object at the
+ time of the spawn.
+14Sep07: Heartbeats weren't working with EPOLL. Noticed by Brian Candler.
+15Sep07: Added some features, tests and documents to Deferrable.
+16Sep07: Added [:content] parameter to EM::Protocols::SmtpClient#send.
+16Sep07: Bumped version to 0.9.0 in anticipation of a release.
+18Sep07: Released version 0.9.0.
+19Sep07: Added #receive_reset to EM::Protocols::SmtpServer.
+19Sep07: User overrides of EM::Protocols::SmtpServer#receive_recipient can now
+ return a Deferrable. Also fixed bug: SmtpClient now raises a protocol
+ error if none of its RCPT TO: commands are accepted by the server.
+26Sep07: Fixed missing keyboard support for Windows.
+03Oct07: Added a default handler for RuntimeErrors emitted from user-written
+ code. Suggested by Brian Candler.
+19Oct07: Set the SO_BROADCAST option automatically on all UDP sockets.
+10Nov07: Forced integer conversion of send_datagram's port parameter.
+Suggested by Matthieu Riou.
+12Nov07: Added saslauth.rb, a protocol module to replace the Cyrus SASL
+daemons saslauthd and pwcheck.
+15Nov07: Fixed bug reported by Mark Zvillius. We were failing to dispatch
+ zero-length datagrams under certain conditions.
+19Nov07: Added EventMachine#set_max_timers. Requested by Matthieu Riou and
+ others.
+19Nov07: Fixed bug with EM::Connection#start_tls. Was not working with server
+ connections. Reported by Michael S. Fischer.
+26Nov07: Supported a hack for EventMachine#popen so it can return an exit
+ status from subprocesses. Requested by Michael S. Fischer.
+30Nov07: Changed Pipe descriptors so that the child-side of the socketpair is
+ NOT set nonblocking. Suggested by Duane Johnson.
+05Dec07: Re-enabled the pure-Ruby implementation.
+06Dec07: Released Version 0.10.0.
+13Dec07: Added EM::DeferrableChildProcess
+24Dec07: Added a SASL client for simple password authentication.
+27Dec07: Removed the hookable error handler. No one was using it and it significantly
+ degraded performance.
+30Dec07: Implemented Kqueue support for OSX and BSD.
+04Jan08: Fixed bug in epoll ("Bad file descriptor"), patch supplied by Chris
+ Heath.
+04Jan08: Fixed bug reported by Michael S. Fischer. We were terminating
+ SSL connections that sent data before the handshake was complete.
+08Jan08: Added an OpenBSD branch for extconf.rb, contributed by Guillaume
+ Sellier.
+19Jan08: Added EM::Connection::get_sockname per request by Michael Fischer.
+19Jan08: Supported IPv6 addresses.
+30Apr08: Set the NODELAY option on sockets that we connect to other servers.
+ Omission noted by Roger Pack.
+14May08: Generated a 0.12 release.
+15May08: Supported EM#get_sockname for acceptors (TCP server sockets).
+ Requested by Roger Pack.
+15May08; Accepted a patch from Dan Aquino that allows the interval of a
+ PeriodicTimer to be changed on the fly.
+15Jun08: Supported nested calls to EM#run. Many people contributed ideas to
+ this, notably raggi and tmm1.
+20Jul08: Accepted patch from tmm1 for EM#fork_reactor.
+28Jul08: Added a Postgres3 implementation, written by FCianfrocca.
+14Aug08: Added a patch by Mike Murphy to support basic auth in the http
+client.
+28Aug08: Added a patch by tmm1 to fix a longstanding problem with Java
+data-sends.
+13Sep08: Added LineText2#set_binary_mode, a back-compatibility alias.
+13Sep08: Modified the load order of protocol libraries in eventmachine.rb
+ to permit a modification of HeaderAndContentProtocol.
+13Sep08: Modified HeaderAndContent to use LineText2, which is less buggy
+ than LineAndTextProtocol. This change may be reversed if we can fix
+ the bugs in buftok.
+13Sep08: Improved the password handling in the Postgres protocol handler.
+15Sep08: Added attach/detach, contributed by Aman Gupta (tmm1) and Riham Aldakkak,
+ to support working with file descriptors not created in the reactor.
+16Sep08: Added an optional version string to the HTTP client. This is a hack
+ that allows a client to specify a version 1.0 request, which
+ keeps the server from sending a chunked response. The right way to
+ solve this, of course, is to support chunked responses.
+23Sep08: ChangeLog Summary for Merge of branches/raggi
+Most notable work and patches by Aman Gupta, Roger Pack, and James Tucker.
+Patches / Tickets also submitted by: Jeremy Evans, aanand, darix, mmmurf,
+danielaquino, macournoyer.
+ - Moved docs into docs/ dir
+ - Major refactor of rakefile, added generic rakefile helpers in tasks
+ - Added example CPP build rakefile in tasks/cpp.rake
+ - Moved rake tests out to tasks/tests.rake
+ - Added svn ignores where appropriate
+ - Fixed jruby build on older java platforms
+ - Gem now builds from Rakefile rather than directly via extconf
+ - Gem unified for jruby, C++ and pure ruby.
+ - Correction for pure C++ build, removing ruby dependency
+ - Fix for CYGWIN builds on ipv6
+ - Major refactor for extconf.rb
+ - Working mingw builds
+ - extconf optionally uses pkg_config over manual configuration
+ - extconf builds for 1.9 on any system that has 1.9
+ - extconf no longer links pthread explicitly
+ - looks for kqueue on all *nix systems
+ - better error output on std::runtime_error, now says where it came from
+ - Fixed some tests on jruby
+ - Added test for general send_data flaw, required for a bugfix in jruby build
+ - Added timeout to epoll tests
+ - Added fixes for java reactor ruby api
+ - Small addition of some docs in httpclient.rb and httpcli2.rb
+ - Some refactor and fixes in smtpserver.rb
+ - Added parenthesis where possible to avoid excess ruby warnings
+ - Refactor of $eventmachine_library logic for accuracy and maintenance, jruby
+ - EM::start_server now supports unix sockets
+ - EM::connect now supports unix sockets
+ - EM::defer @threadqueue now handled more gracefully
+ - Added better messages on exceptions raised
+ - Fix edge case in timer fires
+ - Explicitly require buftok.rb
+ - Add protocols to autoload, rather than require them all immediately
+ - Fix a bug in pr_eventmachine for outbound_q
+ - Refactors to take some of the use of defer out of tests.
+ - Fixes in EM.defer under start/stop conditions. Reduced scope of threads.
+23Sep08: Added patch from tmm1 to avoid popen errors on exit.
+30Sep08: Added File.exists? checks in the args for start_tls, as suggested by
+ Brian Lopez (brianmario).
+10Nov08: ruby 1.9 compatibility enhancements
+28Nov08: Allow for older ruby builds where RARRAY_LEN is not defined
+03Dec08: allow passing arguments to popen handlers
+13Jan09: SSL support for httpclient2 (David Smalley)
+22Jan09: Fixed errors on OSX with the kqueue reactor, fixed errors in the pure
+ ruby reactor. Added EM.current_time. Added EM.epoll? and EM.kqueue?
+27Jan09: Reactor errors are now raised as ruby RuntimeErrors.
+28Jan09: Documentation patch from alloy
+29Jan09: (Late sign-off) Use a longer timeout for connect_server (Ilya
+ Grigorik)
+07Feb09: Fix signal handling issues with threads+epoll
+07Feb09: Use rb_thread_schedule in the epoll reactor
+07Feb09: Use TRAP_BEG/END and rb_thread_schedule in kqueue reactor
+08Feb09: Added fastfilereader from swiftiply
+08Feb09: 1.9 fix for rb_trap_immediate
+08Feb09: Enable rb_thread_blocking_region for 1.9.0 and 1.9.1
+10Feb09: Support win32 builds for fastfilereader
+10Feb09: Added a new event to indicate completion of SSL handshake on TCP
+ connections
+10Feb09: Working get_peer_cert method. Returns the certificate as a Ruby
+ String in PEM format. (Jake Douglas)
+10Feb09: Added EM.get_max_timers
+11Feb09: Fix compile options for sun compiler (Alasdairrr)
+11Feb09: get_status returns a Process::Status object
+12Feb09: Add EM::Protocols::Memcache with simple get/set functionality
+19Feb09: Add catch-all EM.error_handler
+20Feb09: Support miniunit (1.9)
+20Feb09: Return success on content-length = 0 instead of start waiting forever
+ (Ugo Riboni)
+25Feb09: Allow next_tick to be used to pre-schedule reactor operations before
+ EM.run
+26Feb09: Added EM.get_connection_count
+01Mar09: Switch back to extconf for compiling gem extensions
+01Mar09: fixed a small bug with basic auth (mmmurf)
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES
new file mode 100644
index 0000000..6e8856c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/DEFERRABLES
@@ -0,0 +1,246 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency
+to the Ruby programmer's toolbox: spawned processes and deferrables. This
+note will show you how to use deferrables. For more information, see the
+separate document LIGHTWEIGHT_CONCURRENCY.
+
+=== What are Deferrables?
+
+EventMachine's Deferrable borrows heavily from the "deferred" object in
+Python's "Twisted" event-handling framework. Here's a minimal example that
+illustrates Deferrable:
+
+ require 'eventmachine'
+
+ class MyClass
+ include EM::Deferrable
+
+ def print_value x
+ puts "MyClass instance received #{x}"
+ end
+ end
+
+ EM.run {
+ df = MyClass.new
+ df.callback {|x|
+ df.print_value(x)
+ EM.stop
+ }
+
+ EM::Timer.new(2) {
+ df.set_deferred_status :succeeded, 100
+ }
+ }
+
+
+This program will spin for two seconds, print out the string "MyClass
+instance received 100" and then exit. The Deferrable pattern relies on
+an unusual metaphor that may be unfamiliar to you, unless you've used
+Python's Twisted. You may need to read the following material through
+more than once before you get the idea.
+
+EventMachine::Deferrable is simply a Ruby Module that you can include
+in your own classes. (There also is a class named
+EventMachine::DefaultDeferrable for when you want to create one without
+including it in code of your own.)
+
+An object that includes EventMachine::Deferrable is like any other Ruby
+object: it can be created whenever you want, returned from your functions,
+or passed as an argument to other functions.
+
+The Deferrable pattern allows you to specify any number of Ruby code
+blocks (callbacks or errbacks) that will be executed at some future time
+when the status of the Deferrable object changes.
+
+How might that be useful? Well, imagine that you're implementing an HTTP
+server, but you need to make a call to some other server in order to fulfill
+a client request.
+
+When you receive a request from one of your clients, you can create and
+return a Deferrable object. Some other section of your program can add a
+callback to the Deferrable that will cause the client's request to be
+fulfilled. Simultaneously, you initiate an event-driven or threaded client
+request to some different server. And then your EM program will continue to
+process other events and service other client requests.
+
+When your client request to the other server completes some time later, you
+will call the #set_deferred_status method on the Deferrable object, passing
+either a success or failure status, and an arbitrary number of parameters
+(which might include the data you received from the other server).
+
+At that point, the status of the Deferrable object becomes known, and its
+callback or errback methods are immediately executed. Callbacks and errbacks
+are code blocks that are attached to Deferrable objects at any time through
+the methods #callback and #errback.
+
+The deep beauty of this pattern is that it decouples the disposition of one
+operation (such as a client request to an outboard server) from the
+subsequent operations that depend on that disposition (which may include
+responding to a different client or any other operation).
+
+The code which invokes the deferred operation (that will eventually result
+in a success or failure status together with associated data) is completely
+separate from the code which depends on that status and data. This achieves
+one of the primary goals for which threading is typically used in
+sophisticated applications, with none of the nondeterminacy or debugging
+difficulties of threads.
+
+As soon as the deferred status of a Deferrable becomes known by way of a call
+to #set_deferred_status, the Deferrable will IMMEDIATELY execute all of its
+callbacks or errbacks in the order in which they were added to the Deferrable.
+
+Callbacks and errbacks can be added to a Deferrable object at any time, not
+just when the object is created. They can even be added after the status of
+the object has been determined! (In this case, they will be executed
+immediately when they are added.)
+
+A call to Deferrable#set_deferred_status takes :succeeded or :failed as its
+first argument. (This determines whether the object will call its callbacks
+or its errbacks.) #set_deferred_status also takes zero or more additional
+parameters, that will in turn be passed as parameters to the callbacks or
+errbacks.
+
+In general, you can only call #set_deferred_status ONCE on a Deferrable
+object. A call to #set_deferred_status will not return until all of the
+associated callbacks or errbacks have been called. If you add callbacks or
+errbacks AFTER making a call to #set_deferred_status, those additional
+callbacks or errbacks will execute IMMEDIATELY. Any given callback or
+errback will be executed AT MOST once.
+
+It's possible to call #set_deferred_status AGAIN, during the execution a
+callback or errback. This makes it possible to change the parameters which
+will be sent to the callbacks or errbacks farther down the chain, enabling
+some extremely elegant use-cases. You can transform the data returned from
+a deferred operation in arbitrary ways as needed by subsequent users, without
+changing any of the code that generated the original data.
+
+A call to #set_deferred_status will not return until all of the associated
+callbacks or errbacks have been called. If you add callbacks or errbacks
+AFTER making a call to #set_deferred_status, those additional callbacks or
+errbacks will execute IMMEDIATELY.
+
+Let's look at some more sample code. It turns out that many of the internal
+protocol implementations in the EventMachine package rely on Deferrable. One
+of these is EM::Protocols::HttpClient.
+
+To make an evented HTTP request, use the module function
+EM::Protocols::HttpClient#request, which returns a Deferrable object.
+Here's how:
+
+ require 'eventmachine'
+
+ EM.run {
+ df = EM::Protocols::HttpClient.request( :host=>"www.example.com",
+ :request=>"/index.html" )
+
+ df.callback {|response|
+ puts "Succeeded: #{response[:content]}"
+ EM.stop
+ }
+
+ df.errback {|response|
+ puts "ERROR: #{response[:status]}"
+ EM.stop
+ }
+ }
+
+(See the documentation of EventMachine::Protocols::HttpClient for information
+on the object returned by #request.)
+
+In this code, we make a call to HttpClient#request, which immediately returns
+a Deferrable object. In the background, an HTTP client request is being made
+to www.example.com, although your code will continue to run concurrently.
+
+At some future point, the HTTP client request will complete, and the code in
+EM::Protocols::HttpClient will process either a valid HTTP response (including
+returned content), or an error.
+
+At that point, EM::Protocols::HttpClient will call
+EM::Deferrable#set_deferred_status on the Deferrable object that was returned
+to your program, as the return value from EM::Protocols::HttpClient.request.
+You don't have to do anything to make this happen. All you have to do is tell
+the Deferrable what to do in case of either success, failure, or both.
+
+In our code sample, we set one callback and one errback. The former will be
+called if the HTTP call succeeds, and the latter if it fails. (For
+simplicity, we have both of them calling EM#stop to end the program, although
+real programs would be very unlikely to do this.)
+
+Setting callbacks and errbacks is optional. They are handlers to defined
+events in the lifecycle of the Deferrable event. It's not an error if you
+fail to set either a callback, an errback, or both. But of course your
+program will then fail to receive those notifications.
+
+If through some bug it turns out that #set_deferred_status is never called
+on a Deferrable object, then that object's callbacks or errbacks will NEVER
+be called. It's also possible to set a timeout on a Deferrable. If the
+timeout elapses before any other call to #set_deferred_status, the Deferrable
+object will behave as is you had called set_deferred_status(:failed) on it.
+
+
+Now let's modify the example to illustrate some additional points:
+
+ require 'eventmachine'
+
+ EM.run {
+ df = EM::Protocols::HttpClient.request( :host=>"www.example.com",
+ :request=>"/index.html" )
+
+ df.callback {|response|
+ df.set_deferred_status :succeeded, response[:content]
+ }
+
+ df.callback {|string|
+ puts "Succeeded: #{string}"
+ EM.stop
+ }
+
+ df.errback {|response|
+ puts "ERROR: #{response[:status]}"
+ EM.stop
+ }
+ }
+
+
+Just for the sake of illustration, we've now set two callbacks instead of
+one. If the deferrable operation (the HTTP client-request) succeeds, then
+both of the callbacks will be executed in order.
+
+But notice that we've also made our own call to #set_deferred_status in the
+first callback. This isn't required, because the HttpClient implementation
+already made a call to #set_deferred_status. (Otherwise, of course, the
+callback would not be executing.)
+
+But we used #set_deferred_status in the first callback in order to change the
+parameters that will be sent to subsequent callbacks in the chain. In this
+way, you can construct powerful sequences of layered functionality. If you
+want, you can even change the status of the Deferrable from :succeeded to
+:failed, which would abort the chain of callback calls, and invoke the chain
+of errbacks instead.
+
+Now of course it's somewhat trivial to define two callbacks in the same
+method, even with the parameter-changing effect we just described. It would
+be much more interesting to pass the Deferrable to some other function (for
+example, a function defined in another module or a different gem), that would
+in turn add callbacks and/or errbacks of its own. That would illustrate the
+true power of the Deferrable pattern: to isolate the HTTP client-request
+from other functions that use the data that it returns without caring where
+those data came from.
+
+Remember that you can add a callback or an errback to a Deferrable at any
+point in time, regardless of whether the status of the deferred operation is
+known (more precisely, regardless of when #set_deferred_status is called on
+the object). Even hours or days later.
+
+When you add a callback or errback to a Deferrable object on which
+#set_deferred_status has not yet been called, the callback/errback is queued
+up for future execution, inside the Deferrable object. When you add a
+callback or errback to a Deferrable on which #set_deferred_status has
+already been called, the callback/errback will be executed immediately.
+Your code doesn't have to worry about the ordering, and there are no timing
+issues, as there would be with a threaded approach.
+
+For more information on Deferrables and their typical usage patterns, look
+in the EM unit tests. There are also quite a few sugarings (including
+EM::Deferrable#future) that make typical Deferrable usages syntactically
+easier to work with.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/EPOLL b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/EPOLL
new file mode 100644
index 0000000..13cea8f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/EPOLL
@@ -0,0 +1,141 @@
+EventMachine now supports epoll, bringing large increases in performance and scalability to Ruby programs.
+
+Epoll(7) is a alternative mechanism for multiplexed I/O that is available in Linux 2.6 kernels.
+It features significantly greater performance than the standard select(2) mechanism, when used in
+applications that require very large numbers of open I/O descriptors.
+
+EventMachine has always used select(2) because its behavior is well standardized and broadly supported.
+But select becomes unreasonably slow when a program has a
+very large number of file descriptors or sockets. Ruby's version of select hardcodes a limit
+of 1024 descriptors per process, but heavily loaded processes will start to show performance
+degradation even after only a few hundred descriptors are in use.
+
+Epoll is an extended version of the poll(2) call, and it solves the problems with select. Programs
+based on epoll can easily scale past Ruby's 1024-descriptor limit, potentially to tens of thousands
+of connectors, with no significant impact on performance.
+
+(Another alternative which is very similar to epoll in principle is kqueue, supplied on BSD and its
+variants.)
+
+
+
+This note shows you how to use epoll in your programs.
+
+=== Compiling EventMachine to use epoll.
+
+You don't have to do anything to get epoll support in EventMachine.
+When you compile EventMachine on a platform that supports epoll, EM will
+automatically generate a Makefile that includes epoll. (At this writing, this will only work
+on Linux 2.6 kernels.) If you compile EM on a platform without epoll, then epoll support will
+be omitted from the Makefile, and EM will work just as it always has.
+
+=== Using epoll in your programs.
+
+First, you need to tell EventMachine to use epoll instead of select (but see below, as this requirement
+will be removed in a future EventMachine version). Second, you need to prepare your program to use
+more than 1024 descriptors, an operation that generally requires superuser privileges. Third, you will probably
+want your process to drop the superuser privileges after you increase your process's descriptor limit.
+
+=== Using EventMachine#epoll
+
+Call the method EventMachine#epoll anytime before you call EventMachine#run, and your program will
+automatically use epoll, if available. It's safe to call EventMachine#epoll on any platform because
+it compiles to a no-op on platforms that don't support epoll.
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ EM.epoll
+ EM.run {
+ ...
+ }
+
+
+EventMachine#epoll was included in this initial release only to avoid changing the behavior of existing
+programs. However, it's expected that a future release of EM will convert EventMachine#epoll to a no-op,
+and run epoll by default on platforms that support it.
+
+=== Using EventMachine#set_descriptor_table_size
+
+In Linux (as in every Unix-like platform), every process has a internal table that determines the maximum
+number of file and socket descriptors you may have open at any given time. The size of this table is
+generally fixed at 1024, although it may be increased within certain system-defined hard and soft limits.
+
+If you want your EventMachine program to support more than 1024 total descriptors, you must use
+EventMachine#set_descriptor_table_size, as follows:
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ new_size = EM.set_descriptor_table_size( 60000 )
+ $>.puts "New descriptor-table size is #{new_size}"
+
+ EM.run {
+ ...
+ }
+
+If successful, this example will increase the maximum number of descriptors that epoll can use to 60,000.
+Call EventMachine#set_descriptor_table_size without an argument at any time to find out the current
+size of the descriptor table.
+
+Using EventMachine#set_descriptor_table_size ONLY affects the number of descriptors that can be used
+by epoll. It has no useful effect on platforms that don't support epoll, and it does NOT increase the
+number of descriptors that Ruby's own I/O functions can use.
+
+#set_descriptor_table_size can fail if your process is not running as superuser, or if you try to set a
+table size that exceeds the hard limits imposed by your system. In the latter case, try a smaller number.
+
+
+=== Using EventMachine#set_effective_user
+
+In general, you must run your program with elevated or superuser privileges if you want to increase
+your descriptor-table size beyond 1024 descriptors. This is easy enough to verify. Try running the
+sample program given above, that increases the descriptor limit to 60,000. You will probably find that
+the table size will not be increased if you don't run your program as root or with elevated privileges.
+
+But of course network servers, especially long-running ones, should not run with elevated privileges.
+You will want to drop superuser privileges as soon as possible after initialization. To do this,
+use EventMachine#set_effective_user:
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ # (Here, program is running as superuser)
+
+ EM.set_descriptor_table_size( 60000 )
+ EM.set_effective_user( "nobody" )
+ # (Here, program is running as nobody)
+
+ EM.run {
+ ...
+ }
+
+Of course, you will need to replace "nobody" in the example with the name of an unprivileged user
+that is valid on your system. What if you want to drop privileges after opening a server socket
+on a privileged (low-numbered) port? Easy, just call #set_effective_user after opening your sockets:
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ # (Here, program is running as superuser)
+
+ EM.set_descriptor_table_size( 60000 )
+
+ EM.run {
+ EM.start_server( "0.0.0.0", 80, MyHttpServer )
+ EM.start_server( "0.0.0.0", 443, MyEncryptedHttpServer )
+
+ EM.set_effective_user( "nobody" )
+ # (Here, program is running as nobody)
+
+ ...
+ }
+
+
+Because EventMachine#set_effective_user is used to enforce security
+requirements, it has no nonfatal errors. If you try to set a nonexistent or invalid effective user,
+#set_effective_user will abort your program, rather than continue to run with elevated privileges.
+
+EventMachine#set_effective_user is a silent no-op on platforms that don't support it, such as Windows.
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/INSTALL b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/INSTALL
new file mode 100644
index 0000000..dee2e42
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/INSTALL
@@ -0,0 +1,13 @@
+If you have obtained an EventMachine source-tarball (.tar.gz):
+unzip and untar the tarball, and enter the directory that is
+created. In that directory, say:
+ruby setup.rb
+(You may need to be root to execute this command.)
+
+To create documentation for EventMachine, simply type:
+rake rdoc
+in the distro directory. Rdocs will be created in subdirectory rdoc.
+
+If you have obtained a gem version of EventMachine, install it in the
+usual way (gem install eventmachine). You may need superuser privileges
+to execute this command.
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/KEYBOARD b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/KEYBOARD
new file mode 100644
index 0000000..6c699e4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/KEYBOARD
@@ -0,0 +1,42 @@
+EventMachine (EM) can respond to keyboard events. This gives your event-driven
+programs the ability to respond to input from local users.
+
+Programming EM to handle keyboard input in Ruby is simplicity itself. Just use
+EventMachine#open_keyboard, and supply the name of a Ruby module or class that
+will receive the input:
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ module MyKeyboardHandler
+ def receive_data keystrokes
+ puts "I received the following data from the keyboard: #{keystrokes}"
+ end
+ end
+
+ EM.run {
+ EM.open_keyboard(MyKeyboardHandler)
+ }
+
+If you want EM to send line-buffered keyboard input to your program, just
+include the LineText2 protocol module in your handler class or module:
+
+ require 'rubygems'
+ require 'eventmachine'
+
+ module MyKeyboardHandler
+ include EM::Protocols::LineText2
+ def receive_line data
+ puts "I received the following line from the keyboard: #{data}"
+ end
+ end
+
+ EM.run {
+ EM.open_keyboard(MyKeyboardHandler)
+ }
+
+As we said, simplicity itself. You can call EventMachine#open_keyboard at any
+time while the EM reactor loop is running. In other words, the method
+invocation may appear anywhere in an EventMachine#run block, or in any code
+invoked in the #run block.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LEGAL b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LEGAL
new file mode 100644
index 0000000..ee01825
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LEGAL
@@ -0,0 +1,25 @@
+LEGAL NOTICE INFORMATION
+------------------------
+
+EventMachine is Copyright (C) 2006-07 by Francis Cianfrocca.
+
+EventMachine is copyrighted software owned by Francis Cianfrocca
+(blackhedd ... gmail.com). You may redistribute and/or modify this
+software as long as you comply with either the terms of the GPL
+(see the file GPL), or Ruby's license (see the file COPYING).
+
+Your use of all the files in this distribution is controlled by these
+license terms, except for those files specifically mentioned below:
+
+
+
+setup.rb
+ This file is Copyright (C) 2000-2005 by Minero Aoki
+ You can distribute/modify this file under the terms of
+ the GNU LGPL, Lesser General Public License version 2.1.
+
+
+lib/em/buftok.rb
+ This file is Copyright (C) 2007 by Tony Arcieri. This file is
+ covered by the terms of Ruby's License (see the file COPYING).
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LIGHTWEIGHT_CONCURRENCY b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LIGHTWEIGHT_CONCURRENCY
new file mode 100644
index 0000000..3c2cfa0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/LIGHTWEIGHT_CONCURRENCY
@@ -0,0 +1,130 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency to
+the Ruby programmer's toolbox: spawned processes and deferrables. This note
+will show you how to use them.
+
+
+=== What is Lightweight Concurrency?
+
+We use the term "Lightweight Concurrency" (LC) to refer to concurrency
+mechanisms that are lighter than Ruby threads. By "lighter," we mean: less
+resource-intensive in one or more dimensions, usually including memory and
+CPU usage. In general, you turn to LC in the hope of improving the
+performance and scalability of your programs.
+
+In addition to the two EventMachine mechanisms we will discuss here, Ruby
+has at least one other LC construct: Fibers, which are currently under
+development in Ruby 1.9.
+
+The technical feature that makes all of these LC mechanisms different from
+standard Ruby threads is that they are not scheduled automatically.
+
+When you create and run Ruby threads, you can assume (within certain
+constraints) that your threads will all be scheduled fairly by Ruby's runtime.
+Ruby itself is responsible for giving each of your threads its own share of
+the total runtime.
+
+But with LC, your program is responsible for causing different execution
+paths to run. In effect, your program has to act as a "thread scheduler."
+Scheduled entities in LC run to completion and are never preempted. The
+runtime system has far less work to do since it has no need to interrupt
+threads or to schedule them fairly. This is what makes LC lighter and faster.
+
+You'll learn exactly how LC scheduling works in practice as we work through
+specific examples.
+
+
+=== EventMachine Lightweight Concurrency
+
+Recall that EM provides a reactor loop that must be running in order for
+your programs to perform event-driven logic. An EM program typically has a
+structure like this:
+
+ require 'eventmachine'
+
+ # your initializations
+
+ EM.run {
+ # perform event-driven I/O here, including network clients,
+ # servers, timers, and thread-pool operations.
+ }
+
+ # your cleanup
+ # end of the program
+
+
+EventMachine#run executes the reactor loop, which causes your code to be
+called as events of interest to your program occur. The block you pass to
+EventMachine#run is executed right after the reactor loop starts, and is
+the right place to start socket acceptors, etc.
+
+Because the reactor loop runs constantly in an EM program (until it is
+stopped by a call to EventMachine#stop), it has the ability to schedule
+blocks of code for asynchronous execution. Unlike a pre-emptive thread
+scheduler, it's NOT able to interrupt code blocks while they execute. But
+the scheduling capability it does have is enough to enable lightweight
+concurrency.
+
+
+For information on Spawned Processes, see the separate document
+SPAWNED_PROCESSES.
+
+For information on Deferrables, see the separate document DEFERRABLES.
+
+
+=== [SIDEBAR]: I Heard That EventMachine Doesn't Work With Ruby Threads.
+
+This is incorrect. EM is fully interoperable with all versions of Ruby
+threads, and has been since its earliest releases.
+
+It's very true that EM encourages an "evented" (non-threaded) programming
+style. The specific benefits of event-driven programming are far better
+performance and scalability for well-written programs, and far easier
+debugging.
+
+The benefit of using threads for similar applications is a possibly more
+intuitive programming model, as well as the fact that threads are already
+familiar to most programmers. Also, bugs in threaded programs often fail
+to show up until programs go into production. These factors create the
+illusion that threaded programs are easier to write.
+
+However, some operations that occur frequently in professional-caliber
+applications simply can't be done without threads. (The classic example
+is making calls to database client-libraries that block on network I/O
+until they complete.)
+
+EventMachine not only allows the use of Ruby threads in these cases, but
+it even provides a built-in thread-pool object to make them easier to
+work with.
+
+You may have heard a persistent criticism that evented I/O is fundamentally
+incompatible with Ruby threads. It is true that some well-publicized attempts
+to incorporate event-handling libraries into Ruby were not successful. But
+EventMachine was designed from the ground up with Ruby compatibility in mind,
+so EM never suffered from the problems that defeated the earlier attempts.
+
+
+=== [SIDEBAR]: I Heard That EventMachine Doesn't Work Very Well On Windows.
+
+This too is incorrect. EventMachine is an extension written in C++ and Java,
+and therefore it requires compilation. Many Windows computers (and some Unix
+computers, especially in production environments) don't have a build stack.
+Attempting to install EventMachine on a machine without a compiler usually
+produces a confusing error.
+
+In addition, Ruby has a much-debated issue with Windows compiler versions.
+Ruby on Windows works best with Visual Studio 6, a compiler version that is
+long out-of-print, no longer supported by Microsoft, and difficult to obtain.
+(This problem is not specific to EventMachine.)
+
+Shortly after EventMachine was first released, the compiler issues led to
+criticism that EM was incompatible with Windows. Since that time, every
+EventMachine release has been supplied in a precompiled binary form for
+Windows users, that does not require you to compile the code yourself. EM
+binary Gems for Windows are compiled using Visual Studio 6.
+
+EventMachine does supply some advanced features (such as Linux EPOLL support,
+reduced-privilege operation, UNIX-domain sockets, etc.) that have no
+meaningful implementation on Windows. Apart from these special cases, all EM
+functionality (including lightweight concurrency) works perfectly well on
+Windows.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/PURE_RUBY b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/PURE_RUBY
new file mode 100644
index 0000000..157d59e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/PURE_RUBY
@@ -0,0 +1,75 @@
+EventMachine is supplied in three alternative versions.
+
+1) A version that includes a Ruby extension written in C++. This version requires compilation;
+2) A version for JRuby that contains a precompiled JAR file written in Java;
+3) A pure Ruby version that has no external dependencies and can run in any Ruby environment.
+
+The Java version of EventMachine is packaged in a distinct manner and must be installed using a
+special procedure. This version is described fully in a different document, and not considered
+further here.
+
+The C++ and pure-Ruby versions, however, are shipped in the same distribution. You use the same
+files (either tarball or Ruby gem) to install both of these versions.
+
+If you intend to use the C++ version, you must successfully compile EventMachine after you install it.
+(The gem installation attempts to perform this step automatically.)
+
+If you choose not to compile the EventMachine C++ extension, or if your compilation fails for any
+reason, you still have a fully-functional installation of the pure-Ruby version of EM.
+
+However, for technical reasons, a default EM installation (whether or not the compilation succeeds)
+will always assume that the compiled ("extension") implementation should be used.
+
+If you want your EM program to use the pure Ruby version, you must specifically request it. There
+are two ways to do this: by setting either a Ruby global variable, or an environment string.
+
+The following code will invoke the pure-Ruby implementation of EM:
+
+ $eventmachine_library = :pure_ruby
+ require 'eventmachine'
+
+ EM.library_type #=> "pure_ruby"
+
+Notice that this requires a code change and is not the preferred way to select pure Ruby, unless
+for some reason you are absolutely sure you will never want the compiled implementation.
+
+Setting the following environment string has the same effect:
+
+ export EVENTMACHINE_LIBRARY="pure_ruby"
+
+This technique gives you the flexibility to select either version at runtime with no code changes.
+
+Support
+
+The EventMachine development team has committed to support precisely the same APIs for all the
+various implementations of EM.
+
+This means that you can expect any EM program to behave identically, whether you use pure Ruby,
+the compiled C++ extension, or JRuby. Deviations from this behavior are to be considered bugs
+and should be reported as such.
+
+There is a small number of exceptions to this rule, which arise from underlying platform
+distinctions. Notably, EM#epoll is a silent no-op in the pure Ruby implementation.
+
+
+When Should You Use the Pure-Ruby Implementation of EM?
+
+
+Use the pure Ruby implementation of EM when you must support a platform for which no C++ compiler
+is available, or on which the standard EM C++ code can't be compiled.
+
+Keep in mind that you don't need a C++ compiler in order to deploy EM applications that rely on
+the compiled version, so long as appropriate C++ runtime libraries are available on the target platform.
+
+In extreme cases, you may find that you can develop software with the compiled EM version, but are
+not allowed to install required runtime libraries on the deployment system(s). This would be another
+case in which the pure Ruby implementation can be useful.
+
+In general you should avoid the pure Ruby version of EM when performance and scalability are important.
+EM in pure Ruby will necessarily run slower than the compiled version. Depending on your application
+this may or may not be a key issue.
+
+Also, since EPOLL is not supported in pure Ruby, your applications will be affected by Ruby's built-in
+limit of 1024 file and socket descriptors that may be open in a single process. For maximum scalability
+and performance, always use EPOLL if possible.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/RELEASE_NOTES b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/RELEASE_NOTES
new file mode 100644
index 0000000..6110820
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/RELEASE_NOTES
@@ -0,0 +1,94 @@
+RUBY/EventMachine RELEASE NOTES
+
+--------------------------------------------------
+Version: 0.9.0, released xxXXX07
+Added Erlang-like distributed-computing features
+
+--------------------------------------------------
+Version: 0.8.0, released 23Jun07
+Added an epoll implementation for Linux 2.6 kernels.
+Added evented #popen.
+
+--------------------------------------------------
+Version: 0.7.3, released 22May07
+Added a large variety of small features. See the ChangeLog.
+
+--------------------------------------------------
+Version: 0.7.1, released xxNov06
+Added protocol handlers for line-oriented protocols.
+Various bug fixes.
+
+--------------------------------------------------
+Version: 0.7.0, released 20Nov06
+Added a fix in em.cpp/ConnectToServer to fix a fatal exception that
+occurred in FreeBSD when connecting successfully to a remote server.
+
+--------------------------------------------------
+Version: 0.6.0, released xxJul06
+Added deferred operations, suggested by Don Stocks, amillionhitpoints@yahoo.com.
+
+--------------------------------------------------
+Version: 0.5.4, released xxJun06
+Added get_peername support for streams and datagrams.
+
+--------------------------------------------------
+Version: 0.5.3, released 17May06
+Fixed bugs in extconf.rb, thanks to Daniel Harple, dharple@generalconsumption.org.
+Added proper setup.rb and rake tasks, thanks to Austin Ziegler.
+Fixed a handful of reported problems with builds on various platforms.
+
+--------------------------------------------------
+Version: 0.5.2, released 05May06
+Made several nonvisible improvements to the Windows
+implementation.
+Added an exception-handling patch contributed by Jeff Rose, jeff@rosejn.net.
+Added a dir-config patch contributed anonymously.
+Supported builds on Solaris.
+
+--------------------------------------------------
+Version: 0.5.1, released 05May06
+Made it possible to pass a Class rather than a Module
+to a protocol handler.
+Added Windows port.
+
+--------------------------------------------------
+Version: 0.5.0, released 30Apr06
+Added a preliminary SSL/TLS extension. This will probably
+change over the next few releases.
+
+--------------------------------------------------
+Version: 0.4.5, released 29Apr06
+Changed ext files so the ruby.h is installed after unistd.h
+otherwise it doesn't compile on gcc 4.1
+
+--------------------------------------------------
+Version: 0.4.2, released 19Apr06
+Changed the Ruby-glue so the extension will play nicer
+in the sandbox with Ruby threads.
+Added an EventMachine::run_without_threads API to
+switch off the thread-awareness for better performance
+in programs that do not spin any Ruby threads.
+
+--------------------------------------------------
+Version: 0.4.1, released 15Apr06
+Reworked the shared-object interface to make it easier to
+use EventMachine from languages other than Ruby.
+
+--------------------------------------------------
+Version: 0.3.2, released 12Apr06
+Added support for a user-supplied block in EventMachine#connect.
+
+--------------------------------------------------
+Version: 0.3.1, released 11Apr06
+Fixed bug that prevented EventMachine from being run multiple
+times in a single process.
+
+--------------------------------------------------
+Version: 0.3.0, released 10Apr06
+Added method EventHandler::Connection::post_init
+
+--------------------------------------------------
+Version: 0.2.0, released 10Apr06
+Added method EventHandler::stop
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SMTP b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SMTP
new file mode 100644
index 0000000..92bf311
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SMTP
@@ -0,0 +1,4 @@
+This note details the usage of EventMachine's built-in support for SMTP. EM
+supports both client and server connections, which will be described in
+separate sections.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SPAWNED_PROCESSES b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SPAWNED_PROCESSES
new file mode 100644
index 0000000..ee68e3e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/SPAWNED_PROCESSES
@@ -0,0 +1,148 @@
+EventMachine (EM) adds two different formalisms for lightweight concurrency
+to the Ruby programmer's toolbox: spawned processes and deferrables. This
+note will show you how to use spawned processes. For more information, see
+the separate document LIGHTWEIGHT_CONCURRENCY.
+
+
+=== What are Spawned Processes?
+
+Spawned Processes in EventMachine are inspired directly by the "processes"
+found in the Erlang programming language. EM deliberately borrows much (but
+not all) of Erlang's terminology. However, EM's spawned processes differ from
+Erlang's in ways that reflect not only Ruby style, but also the fact that
+Ruby is not a functional language like Erlang.
+
+Let's proceed with a complete, working code sample that we will analyze line
+by line. Here's an EM implementation of the "ping-pong" program that also
+appears in the Erlang tutorial:
+
+
+ require 'eventmachine'
+
+ EM.run {
+ pong = EM.spawn {|x, ping|
+ puts "Pong received #{x}"
+ ping.notify( x-1 )
+ }
+
+ ping = EM.spawn {|x|
+ if x > 0
+ puts "Pinging #{x}"
+ pong.notify x, self
+ else
+ EM.stop
+ end
+ }
+
+ ping.notify 3
+ }
+
+If you run this program, you'll see the following output:
+
+ Pinging 3
+ Pong received 3
+ Pinging 2
+ Pong received 2
+ Pinging 1
+ Pong received 1
+
+Let's take it step by step.
+
+EventMachine#spawn works very much like the built-in function spawn in
+Erlang. It returns a reference to a Ruby object of class
+EventMachine::SpawnedProcess, which is actually a schedulable entity. In
+Erlang, the value returned from spawn is called a "process identifier" or
+"pid." But we'll refer to the Ruby object returned from EM#spawn simply as a
+"spawned process."
+
+You pass a Ruby block with zero or more parameters to EventMachine#spawn.
+Like all Ruby blocks, this one is a closure, so it can refer to variables
+defined in the local context when you call EM#spawn.
+
+However, the code block passed to EM#spawn does NOT execute immediately by
+default. Rather, it will execute only when the Spawned Object is "notified."
+In Erlang, this process is called "message passing," and is done with the
+operator !, but in Ruby it's done simply by calling the #notify method of a
+spawned-process object. The parameters you pass to #notify must match those
+defined in the block that was originally passed to EM#spawn.
+
+When you call the #notify method of a spawned-process object, EM's reactor
+core will execute the code block originally passed to EM#spawn, at some point
+in the future. (#notify itself merely adds a notification to the object's
+message queue and ALWAYS returns immediately.)
+
+When a SpawnedProcess object executes a notification, it does so in the
+context of the SpawnedProcess object itself. The notified code block can see
+local context from the point at which EM#spawn was called. However, the value
+of "self" inside the notified code block is a reference to the SpawnedProcesss
+object itself.
+
+An EM spawned process is nothing more than a Ruby object with a message
+queue attached to it. You can have any number of spawned processes in your
+program without compromising scalability. You can notify a spawned process
+any number of times, and each notification will cause a "message" to be
+placed in the queue of the spawned process. Spawned processes with non-empty
+message queues are scheduled for execution automatically by the EM reactor.
+Spawned processes with no visible references are garbage-collected like any
+other Ruby object.
+
+Back to our code sample:
+
+ pong = EM.spawn {|x, ping|
+ puts "Pong received #{x}"
+ ping.notify( x-1 )
+ }
+
+This simply creates a spawned process and assigns it to the local variable
+pong. You can see that the spawned code block takes a numeric parameter and a
+reference to another spawned process. When pong is notified, it expects to
+receive arguments corresponding to these two parameters. It simply prints out
+the number it receives as the first argument. Then it notifies the spawned
+process referenced by the second argument, passing it the first argument
+minus 1.
+
+And then the block ends, which is crucial because otherwise nothing else
+can run. (Remember that in LC, scheduled entities run to completion and are
+never preempted.)
+
+On to the next bit of the code sample:
+
+ ping = EM.spawn {|x|
+ if x > 0
+ puts "Pinging #{x}"
+ pong.notify x, self
+ else
+ EM.stop
+ end
+ }
+
+Here, we're spawning a process that takes a single (numeric) parameter. If
+the parameter is greater than zero, the block writes it to the console. It
+then notifies the spawned process referenced by the pong local variable,
+passing as arguments its number argument, and a reference to itself. The
+latter reference, as you saw above, is used by pong to send a return
+notification.
+
+If the ping process receives a zero value, it will stop the reactor loop and
+end the program.
+
+Now we've created a pair of spawned processes, but nothing else has happened.
+If we stop now, the program will spin in the EM reactor loop, doing nothing
+at all. Our spawned processes will never be scheduled for execution.
+
+But look at the next line in the code sample:
+
+ ping.notify 3
+
+This line gets the ping-pong ball rolling. We call ping's #notify method,
+passing the argument 3. This causes a message to be sent to the ping spawned
+process. The message contains the single argument, and it causes the EM
+reactor to schedule the ping process. And this in turn results in the
+execution of the Ruby code block passed to EM#spawn when ping was created.
+Everything else proceeds as a result of the messages that are subsequently
+passed to each other by the spawned processes.
+
+[TODO, present the outbound network i/o use case, and clarify that spawned
+processes are interleaved with normal i/o operations and don't interfere
+with them at all. Also, blame Erlang for the confusing term "process"]
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/TODO b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/TODO
new file mode 100644
index 0000000..686a0d5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/docs/old/TODO
@@ -0,0 +1,8 @@
+TODO List:
+
+12Aug06: Noticed by Don Stocks. A TCP connect-request that results
+in a failed DNS resolution fires a fatal error back to user code.
+Uuuuuugly. We should probably cause an unbind event to get fired
+instead, and add some parameterization so the caller can detect
+the nature of the failure.
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/01_eventmachine_echo_server.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/01_eventmachine_echo_server.rb
new file mode 100644
index 0000000..51c5c7d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/01_eventmachine_echo_server.rb
@@ -0,0 +1,18 @@
+#!/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
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, EchoServer)
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb
new file mode 100644
index 0000000..4cfff19
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb
@@ -0,0 +1,22 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class EchoServer < EM::Connection
+ def receive_data(data)
+ if data.strip =~ /exit$/i
+ EventMachine.stop
+ else
+ send_data(data)
+ end
+ end
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, EchoServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/03_simple_chat_server.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/03_simple_chat_server.rb
new file mode 100644
index 0000000..3352551
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/03_simple_chat_server.rb
@@ -0,0 +1,149 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ @@connected_clients = Array.new
+ DM_REGEXP = /^@([a-zA-Z0-9]+)\s*:?\s*(.+)/.freeze
+
+ attr_reader :username
+
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ @username = nil
+
+ puts "A client has connected..."
+ ask_username
+ end
+
+ def unbind
+ @@connected_clients.delete(self)
+ puts "[info] #{@username} has left" if entered_username?
+ end
+
+ def receive_data(data)
+ if entered_username?
+ handle_chat_message(data.strip)
+ else
+ handle_username(data.strip)
+ end
+ end
+
+
+ #
+ # Username handling
+ #
+
+ def entered_username?
+ !@username.nil? && !@username.empty?
+ end # entered_username?
+
+ def handle_username(input)
+ if input.empty?
+ send_line("Blank usernames are not allowed. Try again.")
+ ask_username
+ else
+ @username = input
+ @@connected_clients.push(self)
+ self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") }
+ puts "#{@username} has joined"
+
+ self.send_line("[info] Ohai, #{@username}")
+ end
+ end # handle_username(input)
+
+ def ask_username
+ self.send_line("[info] Enter your username:")
+ end # ask_username
+
+
+ #
+ # Message handling
+ #
+
+ def handle_chat_message(msg)
+ if command?(msg)
+ self.handle_command(msg)
+ else
+ if direct_message?(msg)
+ self.handle_direct_message(msg)
+ else
+ self.announce(msg, "#{@username}:")
+ end
+ end
+ end # handle_chat_message(msg)
+
+ def direct_message?(input)
+ input =~ DM_REGEXP
+ end # direct_message?(input)
+
+ def handle_direct_message(input)
+ username, message = parse_direct_message(input)
+
+ if connection = @@connected_clients.find { |c| c.username == username }
+ puts "[dm] @#{@username} => @#{username}"
+ connection.send_line("[dm] @#{@username}: #{message}")
+ else
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
+ end
+ end # handle_direct_message(input)
+
+ def parse_direct_message(input)
+ return [$1, $2] if input =~ DM_REGEXP
+ end # parse_direct_message(input)
+
+
+ #
+ # Commands handling
+ #
+
+ def command?(input)
+ input =~ /(exit|status)$/i
+ end # command?(input)
+
+ def handle_command(cmd)
+ case cmd
+ when /exit$/i then self.close_connection
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
+ end
+ end # handle_command(cmd)
+
+
+ #
+ # Helpers
+ #
+
+ def announce(msg = nil, prefix = "[chat server]")
+ @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty?
+ end # announce(msg)
+
+ def number_of_connected_clients
+ @@connected_clients.size
+ end # number_of_connected_clients
+
+ def other_peers
+ @@connected_clients.reject { |c| self == c }
+ end # other_peers
+
+ def send_line(line)
+ self.send_data("#{line}\n")
+ end # send_line(line)
+
+ def usernames
+ @@connected_clients.map { |c| c.username }
+ end # usernames
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/04_simple_chat_server_step_one.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/04_simple_chat_server_step_one.rb
new file mode 100644
index 0000000..bb283a7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/04_simple_chat_server_step_one.rb
@@ -0,0 +1,27 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ puts "A client has connected..."
+ end
+
+ def unbind
+ puts "A client has left..."
+ end
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/05_simple_chat_server_step_two.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/05_simple_chat_server_step_two.rb
new file mode 100644
index 0000000..1361c5d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/05_simple_chat_server_step_two.rb
@@ -0,0 +1,43 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ @@connected_clients = Array.new
+
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ @@connected_clients.push(self)
+ puts "A client has connected..."
+ end
+
+ def unbind
+ @@connected_clients.delete(self)
+ puts "A client has left..."
+ end
+
+
+
+
+ #
+ # Helpers
+ #
+
+ def other_peers
+ @@connected_clients.reject { |c| self == c }
+ end # other_peers
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/06_simple_chat_server_step_three.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/06_simple_chat_server_step_three.rb
new file mode 100644
index 0000000..d9b85e2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/06_simple_chat_server_step_three.rb
@@ -0,0 +1,98 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ @@connected_clients = Array.new
+
+
+ attr_reader :username
+
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ @username = nil
+
+ puts "A client has connected..."
+ ask_username
+ end
+
+ def unbind
+ @@connected_clients.delete(self)
+ puts "A client has left..."
+ end
+
+ def receive_data(data)
+ if entered_username?
+ handle_chat_message(data.strip)
+ else
+ handle_username(data.strip)
+ end
+ end
+
+
+
+
+ #
+ # Username handling
+ #
+
+ def entered_username?
+ !@username.nil? && !@username.empty?
+ end # entered_username?
+
+ def handle_username(input)
+ if input.empty?
+ send_line("Blank usernames are not allowed. Try again.")
+ ask_username
+ else
+ @username = input
+ @@connected_clients.push(self)
+ self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") }
+ puts "#{@username} has joined"
+
+ self.send_line("[info] Ohai, #{@username}")
+ end
+ end # handle_username(input)
+
+ def ask_username
+ self.send_line("[info] Enter your username:")
+ end # ask_username
+
+
+
+ #
+ # Message handling
+ #
+
+ def handle_chat_message(msg)
+ raise NotImplementedError
+ end
+
+
+
+ #
+ # Helpers
+ #
+
+ def other_peers
+ @@connected_clients.reject { |c| self == c }
+ end # other_peers
+
+ def send_line(line)
+ self.send_data("#{line}\n")
+ end # send_line(line)
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/07_simple_chat_server_step_four.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/07_simple_chat_server_step_four.rb
new file mode 100644
index 0000000..d4948af
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/07_simple_chat_server_step_four.rb
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ @@connected_clients = Array.new
+
+
+ attr_reader :username
+
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ @username = nil
+
+ puts "A client has connected..."
+ ask_username
+ end
+
+ def unbind
+ @@connected_clients.delete(self)
+ puts "[info] #{@username} has left" if entered_username?
+ end
+
+ def receive_data(data)
+ if entered_username?
+ handle_chat_message(data.strip)
+ else
+ handle_username(data.strip)
+ end
+ end
+
+
+
+
+ #
+ # Username handling
+ #
+
+ def entered_username?
+ !@username.nil? && !@username.empty?
+ end # entered_username?
+
+ def handle_username(input)
+ if input.empty?
+ send_line("Blank usernames are not allowed. Try again.")
+ ask_username
+ else
+ @username = input
+ @@connected_clients.push(self)
+ self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") }
+ puts "#{@username} has joined"
+
+ self.send_line("[info] Ohai, #{@username}")
+ end
+ end # handle_username(input)
+
+ def ask_username
+ self.send_line("[info] Enter your username:")
+ end # ask_username
+
+
+
+ #
+ # Message handling
+ #
+
+ def handle_chat_message(msg)
+ if command?(msg)
+ self.handle_command(msg)
+ else
+ self.announce(msg, "#{@username}:")
+ end
+ end
+
+
+ #
+ # Commands handling
+ #
+
+ def command?(input)
+ input =~ /exit$/i
+ end # command?(input)
+
+ def handle_command(cmd)
+ case cmd
+ when /exit$/i then self.close_connection
+ end
+ end # handle_command(cmd)
+
+
+
+ #
+ # Helpers
+ #
+
+ def announce(msg = nil, prefix = "[chat server]")
+ @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty?
+ end # announce(msg)
+
+ def other_peers
+ @@connected_clients.reject { |c| self == c }
+ end # other_peers
+
+ def send_line(line)
+ self.send_data("#{line}\n")
+ end # send_line(line)
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/08_simple_chat_server_step_five.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/08_simple_chat_server_step_five.rb
new file mode 100644
index 0000000..03da66b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/guides/getting_started/08_simple_chat_server_step_five.rb
@@ -0,0 +1,141 @@
+#!/usr/bin/env ruby
+
+require 'rubygems' # or use Bundler.setup
+require 'eventmachine'
+
+class SimpleChatServer < EM::Connection
+
+ @@connected_clients = Array.new
+ DM_REGEXP = /^@([a-zA-Z0-9]+)\s*:?\s+(.+)/.freeze
+
+ attr_reader :username
+
+
+ #
+ # EventMachine handlers
+ #
+
+ def post_init
+ @username = nil
+
+ puts "A client has connected..."
+ ask_username
+ end
+
+ def unbind
+ @@connected_clients.delete(self)
+ puts "[info] #{@username} has left" if entered_username?
+ end
+
+ def receive_data(data)
+ if entered_username?
+ handle_chat_message(data.strip)
+ else
+ handle_username(data.strip)
+ end
+ end
+
+
+ #
+ # Username handling
+ #
+
+ def entered_username?
+ !@username.nil? && !@username.empty?
+ end # entered_username?
+
+ def handle_username(input)
+ if input.empty?
+ send_line("Blank usernames are not allowed. Try again.")
+ ask_username
+ else
+ @username = input
+ @@connected_clients.push(self)
+ self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") }
+ puts "#{@username} has joined"
+
+ self.send_line("[info] Ohai, #{@username}")
+ end
+ end # handle_username(input)
+
+ def ask_username
+ self.send_line("[info] Enter your username:")
+ end # ask_username
+
+
+ #
+ # Message handling
+ #
+
+ def handle_chat_message(msg)
+ if command?(msg)
+ self.handle_command(msg)
+ else
+ if direct_message?(msg)
+ self.handle_direct_message(msg)
+ else
+ self.announce(msg, "#{@username}:")
+ end
+ end
+ end # handle_chat_message(msg)
+
+ def direct_message?(input)
+ input =~ DM_REGEXP
+ end # direct_message?(input)
+
+ def handle_direct_message(input)
+ username, message = parse_direct_message(input)
+
+ if connection = @@connected_clients.find { |c| c.username == username }
+ puts "[dm] @#{@username} => @#{username}"
+ connection.send_line("[dm] @#{@username}: #{message}")
+ else
+ send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}"
+ end
+ end # handle_direct_message(input)
+
+ def parse_direct_message(input)
+ return [$1, $2] if input =~ DM_REGEXP
+ end # parse_direct_message(input)
+
+
+ #
+ # Commands handling
+ #
+
+ def command?(input)
+ input =~ /(exit|status)$/i
+ end # command?(input)
+
+ def handle_command(cmd)
+ case cmd
+ when /exit$/i then self.close_connection
+ when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room")
+ end
+ end # handle_command(cmd)
+
+
+ #
+ # Helpers
+ #
+
+ def announce(msg = nil, prefix = "[chat server]")
+ @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty?
+ end # announce(msg)
+
+ def other_peers
+ @@connected_clients.reject { |c| self == c }
+ end # other_peers
+
+ def send_line(line)
+ self.send_data("#{line}\n")
+ end # send_line(line)
+end
+
+EventMachine.run do
+ # hit Control + C to stop
+ Signal.trap("INT") { EventMachine.stop }
+ Signal.trap("TERM") { EventMachine.stop }
+
+ EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer)
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_channel.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_channel.rb
new file mode 100644
index 0000000..16e8d08
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_channel.rb
@@ -0,0 +1,43 @@
+require File.dirname(__FILE__) + '/helper'
+
+EM.run do
+
+ # Create a channel to push data to, this could be stocks...
+ RandChannel = EM::Channel.new
+
+ # The server simply subscribes client connections to the channel on connect,
+ # and unsubscribes them on disconnect.
+ class Server < EM::Connection
+ def self.start(host = '127.0.0.1', port = 8000)
+ EM.start_server(host, port, self)
+ end
+
+ def post_init
+ @sid = RandChannel.subscribe { |m| send_data "#{m.inspect}\n" }
+ end
+
+ def unbind
+ RandChannel.unsubscribe @sid
+ end
+ end
+ Server.start
+
+ # Two client connections, that just print what they receive.
+ 2.times do
+ EM.connect('127.0.0.1', 8000) do |c|
+ c.extend EM::P::LineText2
+ def c.receive_line(line)
+ puts "Subscriber: #{signature} got #{line}"
+ end
+ EM.add_timer(2) { c.close_connection }
+ end
+ end
+
+ # This part of the example is more fake, but imagine sleep was in fact a
+ # long running calculation to achieve the value.
+ 40.times do
+ EM.defer lambda { v = sleep(rand * 2); RandChannel << [Time.now, v] }
+ end
+
+ EM.add_timer(5) { EM.stop }
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_queue.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_queue.rb
new file mode 100644
index 0000000..761ea76
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_queue.rb
@@ -0,0 +1,2 @@
+require File.dirname(__FILE__) + '/helper'
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_array.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_array.rb
new file mode 100644
index 0000000..81b0ae3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_array.rb
@@ -0,0 +1,15 @@
+require File.dirname(__FILE__) + '/helper'
+
+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 \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_counter.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_counter.rb
new file mode 100644
index 0000000..58e51ff
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/ex_tick_loop_counter.rb
@@ -0,0 +1,32 @@
+require File.dirname(__FILE__) + '/helper'
+
+class TickCounter
+ attr_reader :start_time, :count
+
+ def initialize
+ reset
+ @tick_loop = EM.tick_loop(method(:tick))
+ end
+
+ def reset
+ @count = 0
+ @start_time = EM.current_time
+ end
+
+ def tick
+ @count += 1
+ end
+
+ def rate
+ @count / (EM.current_time - @start_time)
+ end
+end
+
+period = 5
+EM.run do
+ counter = TickCounter.new
+ EM.add_periodic_timer(period) do
+ puts "Ticks per second: #{counter.rate} (mean of last #{period}s)"
+ counter.reset
+ end
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/helper.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/helper.rb
new file mode 100644
index 0000000..835ded2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/examples/old/helper.rb
@@ -0,0 +1,2 @@
+$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
+require 'eventmachine' \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/Makefile b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/Makefile
new file mode 100644
index 0000000..2221700
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/Makefile
@@ -0,0 +1,273 @@
+
+SHELL = /bin/sh
+
+# V=0 quiet, V=1 verbose. other values don't work.
+V = 0
+V0 = $(V:0=)
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@ :)
+ECHO = $(ECHO1:0=@ echo)
+NULLCMD = :
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0
+hdrdir = $(topdir)
+arch_hdrdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24
+PATH_SEPARATOR = :
+VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
+prefix = $(DESTDIR)/Users/ben/.rubies/ruby-3.4.1
+rubysitearchprefix = $(rubylibprefix)/$(sitearch)
+rubyarchprefix = $(rubylibprefix)/$(arch)
+rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
+exec_prefix = $(prefix)
+vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
+sitearchhdrdir = $(sitehdrdir)/$(sitearch)
+rubyarchhdrdir = $(rubyhdrdir)/$(arch)
+vendorhdrdir = $(rubyhdrdir)/vendor_ruby
+sitehdrdir = $(rubyhdrdir)/site_ruby
+rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
+vendorarchdir = $(vendorlibdir)/$(sitearch)
+vendorlibdir = $(vendordir)/$(ruby_version)
+vendordir = $(rubylibprefix)/vendor_ruby
+sitearchdir = $(sitelibdir)/$(sitearch)
+sitelibdir = $(sitedir)/$(ruby_version)
+sitedir = $(rubylibprefix)/site_ruby
+rubyarchdir = $(rubylibdir)/$(arch)
+rubylibdir = $(rubylibprefix)/$(ruby_version)
+sitearchincludedir = $(includedir)/$(sitearch)
+archincludedir = $(includedir)/$(arch)
+sitearchlibdir = $(libdir)/$(sitearch)
+archlibdir = $(libdir)/$(arch)
+ridir = $(datarootdir)/$(RI_BASE_NAME)
+modular_gc_dir = $(DESTDIR)
+mandir = $(datarootdir)/man
+localedir = $(datarootdir)/locale
+libdir = $(exec_prefix)/lib
+psdir = $(docdir)
+pdfdir = $(docdir)
+dvidir = $(docdir)
+htmldir = $(docdir)
+infodir = $(datarootdir)/info
+docdir = $(datarootdir)/doc/$(PACKAGE)
+oldincludedir = $(DESTDIR)/usr/include
+includedir = $(SDKROOT)$(prefix)/include
+runstatedir = $(localstatedir)/run
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+sysconfdir = $(prefix)/etc
+datadir = $(datarootdir)
+datarootdir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+sbindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/bin
+archdir = $(rubyarchdir)
+
+
+CC_WRAPPER =
+CC = clang
+CXX = clang++ -std=gnu++11
+LIBRUBY = $(LIBRUBY_A)
+LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
+LIBRUBYARG_SHARED =
+LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation $(MAINLIBS)
+empty =
+OUTFLAG = -o $(empty)
+COUTFLAG = -o $(empty)
+CSRCFLAG = $(empty)
+
+RUBY_EXTCONF_H =
+cflags = $(hardenflags) -fdeclspec $(optflags) $(debugflags) $(warnflags)
+cxxflags =
+optflags = -O3 -fno-fast-math
+debugflags = -ggdb3
+warnflags = -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef
+cppflags =
+CCDLFLAGS = -fno-common
+CFLAGS = $(CCDLFLAGS) $(cflags) -pipe $(ARCH_FLAG)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS = -DHAVE_OPENSSL_SSL_H -DHAVE_OPENSSL_ERR_H -DWITH_SSL -DBUILD_FOR_RUBY -DHAVE_RB_THREAD_CALL_WITHOUT_GVL -DHAVE_RB_THREAD_FD_SELECT -DHAVE_TYPE_RB_FDSET_T -DHAVE_RB_WAIT_FOR_SINGLE_FD -DHAVE_RB_TIME_NEW -DHAVE_WRITEV -DOS_UNIX -DHAVE_SYS_EVENT_H -DHAVE_SYS_QUEUE_H -DHAVE_KQUEUE -DOS_DARWIN -DHAVE_CLOCK_GETTIME -DHAVE_CONST_CLOCK_MONOTONIC_RAW -DHAVE_CONST_CLOCK_MONOTONIC -DHAVE_MAKE_PAIR -I/opt/homebrew/opt/openssl@3/include -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/libyaml/include -I/opt/homebrew/opt/gdbm/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
+CXXFLAGS = $(CCDLFLAGS) -fdeclspec $(ARCH_FLAG)
+ldflags = -L. -fstack-protector-strong -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib
+dldflags = -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib -Wl,-undefined,dynamic_lookup -bundle_loader '$(BUILTRUBY)'
+ARCH_FLAG = -arch arm64
+DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
+LDSHARED = $(CXX) -dynamic -bundle
+LDSHAREDXX = $(CXX) -dynamic -bundle
+POSTLINK = dsymutil $@ 2>/dev/null; { test -z '$(RUBY_CODESIGN)' || codesign -s '$(RUBY_CODESIGN)' $@; }
+AR = ar
+LD = ld
+EXEEXT =
+
+RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
+RUBY_SO_NAME = ruby.3.4
+RUBYW_INSTALL_NAME =
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+RUBYW_BASE_NAME = rubyw
+RUBY_BASE_NAME = ruby
+
+arch = arm64-darwin24
+sitearch = $(arch)
+ruby_version = 3.4.0
+ruby = $(bindir)/$(RUBY_BASE_NAME)
+RUBY = $(ruby)
+BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
+ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
+
+RM = rm -f
+RM_RF = rm -fr
+RMDIRS = rmdir -p
+MAKEDIRS = mkdir -p
+INSTALL = /usr/bin/install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+TOUCH = exit >
+
+#### End of system configuration section. ####
+
+preload =
+libpath = . $(libdir) /opt/homebrew/opt/openssl@3/lib /opt/homebrew/opt/readline/lib /opt/homebrew/opt/libyaml/lib /opt/homebrew/opt/gdbm/lib
+LIBPATH = -L. -L$(libdir) -L/opt/homebrew/opt/openssl@3/lib -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/gdbm/lib
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+DISTCLEANDIRS =
+
+extout =
+extout_prefix =
+target_prefix =
+LOCAL_LIBS =
+LIBS = -lcrypto -lssl -lpthread
+ORIG_SRCS = binder.cpp cmain.cpp ed.cpp em.cpp kb.cpp page.cpp pipe.cpp rubymain.cpp ssl.cpp
+SRCS = $(ORIG_SRCS)
+OBJS = binder.o cmain.o ed.o em.o kb.o page.o pipe.o rubymain.o ssl.o
+HDRS = $(srcdir)/binder.h $(srcdir)/ed.h $(srcdir)/em.h $(srcdir)/eventmachine.h $(srcdir)/page.h $(srcdir)/project.h $(srcdir)/ssl.h
+LOCAL_HDRS =
+TARGET = rubyeventmachine
+TARGET_NAME = rubyeventmachine
+TARGET_ENTRY = Init_$(TARGET_NAME)
+DLLIB = $(TARGET).bundle
+EXTSTATIC =
+STATIC_LIB =
+
+TIMESTAMP_DIR = .
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+HDRDIR = $(sitehdrdir)$(target_prefix)
+ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
+TARGET_SO_DIR =
+TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
+CLEANLIBS = $(TARGET_SO) $(TARGET_SO:=.dSYM)
+CLEANOBJS = $(OBJS) *.bak
+TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-static clean-rb
+
+clean-static::
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-static clean-rb-default clean-rb
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
+
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+clean-static::
+ -$(Q)$(RM) $(STATIC_LIB)
+install-rb: pre-install-rb do-install-rb install-rb-default
+install-rb-default: pre-install-rb-default do-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+do-install-rb:
+do-install-rb-default:
+pre-install-rb-default:
+ @$(NULLCMD)
+$(TARGET_SO_DIR_TIMESTAMP):
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
+ $(Q) $(TOUCH) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
+
+.cc.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cc.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.mm.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.mm.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cxx.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cxx.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cpp.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cpp.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.c.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.c.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.m.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.m.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+$(TARGET_SO): $(OBJS) Makefile
+ $(ECHO) linking shared-object $(DLLIB)
+ -$(Q)$(RM) $(@)
+ $(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+ $(Q) $(POSTLINK)
+
+
+
+$(OBJS): $(HDRS) $(ruby_headers)
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.cpp
new file mode 100644
index 0000000..5b90876
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.cpp
@@ -0,0 +1,124 @@
+/*****************************************************************************
+
+$Id$
+
+File: binder.cpp
+Date: 07Apr06
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+
+#define DEV_URANDOM "/dev/urandom"
+
+
+std::map<uintptr_t, Bindable_t*> Bindable_t::BindingBag;
+
+
+/********************************
+STATIC Bindable_t::CreateBinding
+********************************/
+
+uintptr_t Bindable_t::CreateBinding()
+{
+ static uintptr_t num = 0;
+ while(BindingBag[++num]) {}
+ return num;
+}
+
+#if 0
+string Bindable_t::CreateBinding()
+{
+ static int index = 0;
+ static string seed;
+
+ if ((index >= 1000000) || (seed.length() == 0)) {
+ #ifdef OS_UNIX
+ int fd = open (DEV_URANDOM, O_RDONLY);
+ if (fd < 0)
+ throw std::runtime_error ("No entropy device");
+
+ unsigned char u[16];
+ size_t r = read (fd, u, sizeof(u));
+ if (r < sizeof(u))
+ throw std::runtime_error ("Unable to read entropy device");
+
+ unsigned char *u1 = (unsigned char*)u;
+ char u2 [sizeof(u) * 2 + 1];
+
+ for (size_t i=0; i < sizeof(u); i++)
+ sprintf (u2 + (i * 2), "%02x", u1[i]);
+
+ seed = string (u2);
+ #endif
+
+
+ #ifdef OS_WIN32
+ UUID uuid;
+ UuidCreate (&uuid);
+ unsigned char *uuidstring = NULL;
+ UuidToString (&uuid, &uuidstring);
+ if (!uuidstring)
+ throw std::runtime_error ("Unable to read uuid");
+ seed = string ((const char*)uuidstring);
+
+ RpcStringFree (&uuidstring);
+ #endif
+
+ index = 0;
+
+
+ }
+
+ stringstream ss;
+ ss << seed << (++index);
+ return ss.str();
+}
+#endif
+
+/*****************************
+STATIC: Bindable_t::GetObject
+*****************************/
+
+Bindable_t *Bindable_t::GetObject (const uintptr_t binding)
+{
+ std::map<uintptr_t, Bindable_t*>::const_iterator i = BindingBag.find (binding);
+ if (i != BindingBag.end())
+ return i->second;
+ else
+ return NULL;
+}
+
+
+/**********************
+Bindable_t::Bindable_t
+**********************/
+
+Bindable_t::Bindable_t()
+{
+ Binding = Bindable_t::CreateBinding();
+ BindingBag [Binding] = this;
+}
+
+
+
+/***********************
+Bindable_t::~Bindable_t
+***********************/
+
+Bindable_t::~Bindable_t() NO_EXCEPT_FALSE
+{
+ BindingBag.erase (Binding);
+}
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.h
new file mode 100644
index 0000000..dd32c8d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/binder.h
@@ -0,0 +1,52 @@
+/*****************************************************************************
+
+$Id$
+
+File: binder.h
+Date: 07Apr06
+
+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.
+
+*****************************************************************************/
+
+#ifndef __ObjectBindings__H_
+#define __ObjectBindings__H_
+
+
+#if __cplusplus >= 201103L
+#define NO_EXCEPT_FALSE noexcept(false)
+#else
+#define NO_EXCEPT_FALSE
+#endif
+
+class Bindable_t
+{
+ public:
+ static uintptr_t CreateBinding();
+ static Bindable_t *GetObject (const uintptr_t);
+ static std::map<uintptr_t, Bindable_t*> BindingBag;
+
+ public:
+ Bindable_t();
+ virtual ~Bindable_t() NO_EXCEPT_FALSE;
+
+ const uintptr_t GetBinding() {return Binding;}
+
+ private:
+ uintptr_t Binding;
+};
+
+
+
+
+
+#endif // __ObjectBindings__H_
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/cmain.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/cmain.cpp
new file mode 100644
index 0000000..f58c2cd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/cmain.cpp
@@ -0,0 +1,988 @@
+/*****************************************************************************
+
+$Id$
+
+File: cmain.cpp
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+
+/* 21Sep09: ruby 1.9 defines macros for common i/o functions that point to rb_w32_* implementations.
+ We need to undef the stat to fix a build failure in evma_send_file_data_to_connection.
+ See http://groups.google.com/group/eventmachine/browse_thread/thread/fc60d9bb738ffc71
+*/
+#if defined(BUILD_FOR_RUBY) && defined(OS_WIN32)
+#undef stat
+#undef fstat
+#endif
+
+static EventMachine_t *EventMachine;
+static Poller_t Poller = Poller_Default;
+
+extern "C" void ensure_eventmachine (const char *caller = "unknown caller")
+{
+ if (!EventMachine) {
+ const int err_size = 128;
+ char err_string[err_size];
+ snprintf (err_string, err_size, "eventmachine not initialized: %s", caller);
+ #ifdef BUILD_FOR_RUBY
+ rb_raise(rb_eRuntimeError, "%s", err_string);
+ #else
+ throw std::runtime_error (err_string);
+ #endif
+ }
+}
+
+/***********************
+evma_initialize_library
+***********************/
+
+extern "C" void evma_initialize_library (EMCallback cb)
+{
+ if (EventMachine)
+ #ifdef BUILD_FOR_RUBY
+ rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_initialize_library");
+ #else
+ throw std::runtime_error ("eventmachine already initialized: evma_initialize_library");
+ #endif
+
+ EventMachine = new EventMachine_t (cb, Poller);
+}
+
+
+/********************
+evma_release_library
+********************/
+
+extern "C" void evma_release_library()
+{
+ ensure_eventmachine("evma_release_library");
+ delete EventMachine;
+ EventMachine = NULL;
+}
+
+
+/*********************
+evma_run_machine_once
+*********************/
+
+extern "C" bool evma_run_machine_once()
+{
+ ensure_eventmachine("evma_run_machine_once");
+ return EventMachine->RunOnce();
+}
+
+
+/****************
+evma_run_machine
+****************/
+
+extern "C" void evma_run_machine()
+{
+ ensure_eventmachine("evma_run_machine");
+ EventMachine->Run();
+}
+
+
+/**************************
+evma_install_oneshot_timer
+**************************/
+
+extern "C" const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds)
+{
+ ensure_eventmachine("evma_install_oneshot_timer");
+ return EventMachine->InstallOneshotTimer (milliseconds);
+}
+
+
+/**********************
+evma_connect_to_server
+**********************/
+
+extern "C" const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port)
+{
+ ensure_eventmachine("evma_connect_to_server");
+ return EventMachine->ConnectToServer (bind_addr, bind_port, server, port);
+}
+
+/***************************
+evma_connect_to_unix_server
+***************************/
+
+extern "C" const uintptr_t evma_connect_to_unix_server (const char *server)
+{
+ ensure_eventmachine("evma_connect_to_unix_server");
+ return EventMachine->ConnectToUnixServer (server);
+}
+
+/**************
+evma_attach_fd
+**************/
+
+extern "C" const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode)
+{
+ ensure_eventmachine("evma_attach_fd");
+ return EventMachine->AttachFD (file_descriptor, watch_mode ? true : false);
+}
+
+/**************
+evma_detach_fd
+**************/
+
+extern "C" int evma_detach_fd (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_detach_fd");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return EventMachine->DetachFD (ed);
+ else
+ #ifdef BUILD_FOR_RUBY
+ rb_raise(rb_eRuntimeError, "invalid binding to detach");
+ #else
+ throw std::runtime_error ("invalid binding to detach");
+ #endif
+ return -1;
+}
+
+/************************
+evma_get_file_descriptor
+************************/
+
+extern "C" int evma_get_file_descriptor (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_file_descriptor");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetSocket();
+ else
+ #ifdef BUILD_FOR_RUBY
+ rb_raise(rb_eRuntimeError, "invalid binding to get_fd");
+ #else
+ throw std::runtime_error ("invalid binding to get_fd");
+ #endif
+ return -1;
+}
+
+/***********************
+evma_is_notify_readable
+***********************/
+
+extern "C" int evma_is_notify_readable (const uintptr_t binding)
+{
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ return cd->IsNotifyReadable() ? 1 : 0;
+ return -1;
+}
+
+/************************
+evma_set_notify_readable
+************************/
+
+extern "C" void evma_set_notify_readable (const uintptr_t binding, int mode)
+{
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ cd->SetNotifyReadable (mode ? true : false);
+}
+
+/***********************
+evma_is_notify_writable
+***********************/
+
+extern "C" int evma_is_notify_writable (const uintptr_t binding)
+{
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ return cd->IsNotifyWritable() ? 1 : 0;
+ return -1;
+}
+
+/************************
+evma_set_notify_writable
+************************/
+
+extern "C" void evma_set_notify_writable (const uintptr_t binding, int mode)
+{
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ cd->SetNotifyWritable (mode ? true : false);
+}
+
+/**********
+evma_pause
+**********/
+
+extern "C" int evma_pause (const uintptr_t binding)
+{
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ return cd->Pause() ? 1 : 0;
+
+ return 0;
+}
+
+/***********
+evma_resume
+***********/
+
+extern "C" int evma_resume (const uintptr_t binding)
+{
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ return cd->Resume() ? 1 : 0;
+
+ return 0;
+}
+
+/**************
+evma_is_paused
+**************/
+
+extern "C" int evma_is_paused (const uintptr_t binding)
+{
+ EventableDescriptor *cd = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ return cd->IsPaused() ? 1 : 0;
+
+ return 0;
+}
+
+/************************
+evma_num_close_scheduled
+************************/
+
+extern "C" int evma_num_close_scheduled ()
+{
+ ensure_eventmachine("evma_num_close_scheduled");
+ return EventMachine->NumCloseScheduled;
+}
+
+/**********************
+evma_create_tcp_server
+**********************/
+
+extern "C" const uintptr_t evma_create_tcp_server (const char *address, int port)
+{
+ ensure_eventmachine("evma_create_tcp_server");
+ return EventMachine->CreateTcpServer (address, port);
+}
+
+/******************************
+evma_create_unix_domain_server
+******************************/
+
+extern "C" const uintptr_t evma_create_unix_domain_server (const char *filename)
+{
+ ensure_eventmachine("evma_create_unix_domain_server");
+ return EventMachine->CreateUnixDomainServer (filename);
+}
+
+/***********************
+evma_attach_sd
+************************/
+
+extern "C" const uintptr_t evma_attach_sd (int sd)
+{
+ ensure_eventmachine("evma_attach_sd");
+ return EventMachine->AttachSD (sd);
+}
+
+/*************************
+evma_open_datagram_socket
+*************************/
+
+extern "C" const uintptr_t evma_open_datagram_socket (const char *address, int port)
+{
+ ensure_eventmachine("evma_open_datagram_socket");
+ return EventMachine->OpenDatagramSocket (address, port);
+}
+
+/******************
+evma_open_keyboard
+******************/
+
+extern "C" const uintptr_t evma_open_keyboard()
+{
+ ensure_eventmachine("evma_open_keyboard");
+ return EventMachine->OpenKeyboard();
+}
+
+/*******************
+evma_watch_filename
+*******************/
+
+extern "C" const uintptr_t evma_watch_filename (const char *fname)
+{
+ ensure_eventmachine("evma_watch_filename");
+ return EventMachine->WatchFile(fname);
+}
+
+/*********************
+evma_unwatch_filename
+*********************/
+
+extern "C" void evma_unwatch_filename (const uintptr_t sig)
+{
+ ensure_eventmachine("evma_unwatch_file");
+ EventMachine->UnwatchFile(sig);
+}
+
+/**************
+evma_watch_pid
+**************/
+
+extern "C" const uintptr_t evma_watch_pid (int pid)
+{
+ ensure_eventmachine("evma_watch_pid");
+ return EventMachine->WatchPid(pid);
+}
+
+/****************
+evma_unwatch_pid
+****************/
+
+extern "C" void evma_unwatch_pid (const uintptr_t sig)
+{
+ ensure_eventmachine("evma_unwatch_pid");
+ EventMachine->UnwatchPid(sig);
+}
+
+/****************************
+evma_send_data_to_connection
+****************************/
+
+extern "C" int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length)
+{
+ ensure_eventmachine("evma_send_data_to_connection");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->SendOutboundData(data, data_length);
+ return -1;
+}
+
+/******************
+evma_send_datagram
+******************/
+
+extern "C" int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port)
+{
+ ensure_eventmachine("evma_send_datagram");
+ DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding));
+ if (dd)
+ return dd->SendOutboundDatagram(data, data_length, address, port);
+ return -1;
+}
+
+
+/*********************
+evma_close_connection
+*********************/
+
+extern "C" void evma_close_connection (const uintptr_t binding, int after_writing)
+{
+ ensure_eventmachine("evma_close_connection");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ ed->ScheduleClose (after_writing ? true : false);
+}
+
+/***********************************
+evma_report_connection_error_status
+***********************************/
+
+extern "C" int evma_report_connection_error_status (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_report_connection_error_status");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->ReportErrorStatus();
+ return -1;
+}
+
+/********************
+evma_stop_tcp_server
+********************/
+
+extern "C" void evma_stop_tcp_server (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_stop_tcp_server");
+ AcceptorDescriptor::StopAcceptor (binding);
+}
+
+
+/*****************
+evma_stop_machine
+*****************/
+
+extern "C" void evma_stop_machine()
+{
+ ensure_eventmachine("evma_stop_machine");
+ EventMachine->ScheduleHalt();
+}
+
+/*****************
+evma_stopping
+*****************/
+
+extern "C" bool evma_stopping()
+{
+ ensure_eventmachine("evma_stopping");
+ return EventMachine->Stopping();
+}
+
+/**************
+evma_start_tls
+**************/
+
+extern "C" void evma_start_tls (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_start_tls");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ ed->StartTls();
+}
+
+/******************
+evma_set_tls_parms
+******************/
+
+extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version)
+{
+ ensure_eventmachine("evma_set_tls_parms");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version);
+}
+
+/******************
+evma_get_peer_cert
+******************/
+
+#ifdef WITH_SSL
+extern "C" X509 *evma_get_peer_cert (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_peer_cert");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetPeerCert();
+ return NULL;
+}
+#endif
+
+/******************
+evma_get_cipher_bits
+******************/
+
+#ifdef WITH_SSL
+extern "C" int evma_get_cipher_bits (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_cipher_bits");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetCipherBits();
+ return -1;
+}
+#endif
+
+/******************
+evma_get_cipher_name
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_cipher_name (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_cipher_name");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetCipherName();
+ return NULL;
+}
+#endif
+
+/******************
+evma_get_cipher_protocol
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_cipher_protocol");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetCipherProtocol();
+ return NULL;
+}
+#endif
+
+/******************
+evma_get_sni_hostname
+******************/
+
+#ifdef WITH_SSL
+extern "C" const char *evma_get_sni_hostname (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_sni_hostname");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed)
+ return ed->GetSNIHostname();
+ return NULL;
+}
+#endif
+
+/********************
+evma_accept_ssl_peer
+********************/
+
+#ifdef WITH_SSL
+extern "C" void evma_accept_ssl_peer (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_accept_ssl_peer");
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject (binding));
+ if (cd)
+ cd->AcceptSslPeer();
+}
+#endif
+
+/*****************
+evma_get_peername
+*****************/
+
+extern "C" int evma_get_peername (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
+{
+ ensure_eventmachine("evma_get_peername");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ed->GetPeername (sa, len) ? 1 : 0;
+ }
+ else
+ return 0;
+}
+
+/*****************
+evma_get_sockname
+*****************/
+
+extern "C" int evma_get_sockname (const uintptr_t binding, struct sockaddr *sa, socklen_t *len)
+{
+ ensure_eventmachine("evma_get_sockname");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ed->GetSockname (sa, len) ? 1 : 0;
+ }
+ else
+ return 0;
+}
+
+/***********************
+evma_get_subprocess_pid
+***********************/
+
+#ifdef OS_UNIX
+extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid)
+{
+ ensure_eventmachine("evma_get_subprocess_pid");
+ PipeDescriptor *pd = dynamic_cast <PipeDescriptor*> (Bindable_t::GetObject (binding));
+ if (pd) {
+ return pd->GetSubprocessPid (pid) ? 1 : 0;
+ }
+ else if (pid && EventMachine->SubprocessPid) {
+ *pid = EventMachine->SubprocessPid;
+ return 1;
+ }
+ else
+ return 0;
+}
+#else
+extern "C" int evma_get_subprocess_pid (const uintptr_t binding UNUSED, pid_t *pid UNUSED)
+{
+ return 0;
+}
+#endif
+
+/**************************
+evma_get_subprocess_status
+**************************/
+
+extern "C" int evma_get_subprocess_status (const uintptr_t binding UNUSED, int *status)
+{
+ ensure_eventmachine("evma_get_subprocess_status");
+ if (status) {
+ *status = EventMachine->SubprocessExitStatus;
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/*************************
+evma_get_connection_count
+*************************/
+
+extern "C" int evma_get_connection_count()
+{
+ ensure_eventmachine("evma_get_connection_count");
+ return EventMachine->GetConnectionCount();
+}
+
+/*********************
+evma_signal_loopbreak
+*********************/
+
+extern "C" void evma_signal_loopbreak()
+{
+ ensure_eventmachine("evma_signal_loopbreak");
+ EventMachine->SignalLoopBreaker();
+}
+
+
+
+/********************************
+evma_get_comm_inactivity_timeout
+********************************/
+
+extern "C" float evma_get_comm_inactivity_timeout (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_comm_inactivity_timeout");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ((float)ed->GetCommInactivityTimeout() / 1000);
+ }
+ else
+ return 0.0; //Perhaps this should be an exception. Access to an unknown binding.
+}
+
+/********************************
+evma_set_comm_inactivity_timeout
+********************************/
+
+extern "C" int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value)
+{
+ ensure_eventmachine("evma_set_comm_inactivity_timeout");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ed->SetCommInactivityTimeout ((uint64_t)(value * 1000));
+ }
+ else
+ return 0; //Perhaps this should be an exception. Access to an unknown binding.
+}
+
+
+/********************************
+evma_get_pending_connect_timeout
+********************************/
+
+extern "C" float evma_get_pending_connect_timeout (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_pending_connect_timeout");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ((float)ed->GetPendingConnectTimeout() / 1000);
+ }
+ else
+ return 0.0;
+}
+
+
+/********************************
+evma_set_pending_connect_timeout
+********************************/
+
+extern "C" int evma_set_pending_connect_timeout (const uintptr_t binding, float value)
+{
+ ensure_eventmachine("evma_set_pending_connect_timeout");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ if (ed) {
+ return ed->SetPendingConnectTimeout ((uint64_t)(value * 1000));
+ }
+ else
+ return 0;
+}
+
+
+/**********************
+evma_set_timer_quantum
+**********************/
+
+extern "C" void evma_set_timer_quantum (int interval)
+{
+ ensure_eventmachine("evma_set_timer_quantum");
+ EventMachine->SetTimerQuantum (interval);
+}
+
+
+/************************
+evma_get_max_timer_count
+************************/
+
+extern "C" int evma_get_max_timer_count()
+{
+ return EventMachine_t::GetMaxTimerCount();
+}
+
+/************************
+evma_set_max_timer_count
+************************/
+
+extern "C" void evma_set_max_timer_count (int ct)
+{
+ // This may only be called if the reactor is not running.
+
+ if (EventMachine)
+ #ifdef BUILD_FOR_RUBY
+ rb_raise(rb_eRuntimeError, "eventmachine already initialized: evma_set_max_timer_count");
+ #else
+ throw std::runtime_error ("eventmachine already initialized: evma_set_max_timer_count");
+ #endif
+ EventMachine_t::SetMaxTimerCount (ct);
+}
+
+/******************
+evma_get/set_simultaneous_accept_count
+******************/
+
+extern "C" void evma_set_simultaneous_accept_count (int count)
+{
+ EventMachine_t::SetSimultaneousAcceptCount(count);
+}
+
+extern "C" int evma_get_simultaneous_accept_count()
+{
+ return EventMachine_t::GetSimultaneousAcceptCount();
+}
+
+
+/******************
+evma_setuid_string
+******************/
+
+extern "C" void evma_setuid_string (const char *username)
+{
+ // We do NOT need to be running an EM instance because this method is static.
+ EventMachine_t::SetuidString (username);
+}
+
+
+/**********
+evma_popen
+**********/
+
+extern "C" const uintptr_t evma_popen (char * const*cmd_strings)
+{
+ ensure_eventmachine("evma_popen");
+ return EventMachine->Socketpair (cmd_strings);
+}
+
+
+/***************************
+evma_get_outbound_data_size
+***************************/
+
+extern "C" int evma_get_outbound_data_size (const uintptr_t binding)
+{
+ ensure_eventmachine("evma_get_outbound_data_size");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (binding));
+ return ed ? ed->GetOutboundDataSize() : 0;
+}
+
+
+/**************
+evma_set_epoll
+**************/
+
+extern "C" void evma_set_epoll (int use)
+{
+ if (use)
+ Poller = Poller_Epoll;
+ else
+ Poller = Poller_Default;
+}
+
+/***************
+evma_set_kqueue
+***************/
+
+extern "C" void evma_set_kqueue (int use)
+{
+ if (use)
+ Poller = Poller_Kqueue;
+ else
+ Poller = Poller_Default;
+}
+
+
+/**********************
+evma_set_rlimit_nofile
+**********************/
+
+extern "C" int evma_set_rlimit_nofile (int nofiles)
+{
+ return EventMachine_t::SetRlimitNofile (nofiles);
+}
+
+
+/*********************************
+evma_send_file_data_to_connection
+*********************************/
+
+extern "C" int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename)
+{
+ /* This is a sugaring over send_data_to_connection that reads a file into a
+ * locally-allocated buffer, and sends the file data to the remote peer.
+ * Return the number of bytes written to the caller.
+ * TODO, needs to impose a limit on the file size. This is intended only for
+ * small files. (I don't know, maybe 8K or less.) For larger files, use interleaved
+ * I/O to avoid slowing the rest of the system down.
+ * TODO: we should return a code rather than barf, in case of file-not-found.
+ * TODO, does this compile on Windows?
+ * TODO, given that we want this to work only with small files, how about allocating
+ * the buffer on the stack rather than the heap?
+ *
+ * Modified 25Jul07. This now returns -1 on file-too-large; 0 for success, and a positive
+ * errno in case of other errors.
+ *
+ * Contributed by Kirk Haines.
+ */
+
+ char data[32*1024];
+ int r;
+
+ ensure_eventmachine("evma_send_file_data_to_connection");
+
+#if defined(OS_WIN32)
+ int Fd = open (filename, O_RDONLY|O_BINARY);
+#else
+ int Fd = open (filename, O_RDONLY);
+#endif
+ if (Fd < 0)
+ return errno;
+ // From here on, all early returns MUST close Fd.
+
+ struct stat st;
+ if (fstat (Fd, &st)) {
+ int e = errno;
+ close (Fd);
+ return e;
+ }
+
+ off_t filesize = st.st_size;
+ if (filesize <= 0) {
+ close (Fd);
+ return 0;
+ }
+ else if (filesize > (off_t) sizeof(data)) {
+ close (Fd);
+ return -1;
+ }
+
+ r = read (Fd, data, filesize);
+ if (r != filesize) {
+ int e = errno;
+ close (Fd);
+ return e;
+ }
+ evma_send_data_to_connection (binding, data, r);
+ close (Fd);
+
+ return 0;
+}
+
+
+/****************
+evma_start_proxy
+*****************/
+
+extern "C" void evma_start_proxy (const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length)
+{
+ ensure_eventmachine("evma_start_proxy");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+ if (ed)
+ ed->StartProxy(to, bufsize, length);
+}
+
+
+/***************
+evma_stop_proxy
+****************/
+
+extern "C" void evma_stop_proxy (const uintptr_t from)
+{
+ ensure_eventmachine("evma_stop_proxy");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+ if (ed)
+ ed->StopProxy();
+}
+
+/******************
+evma_proxied_bytes
+*******************/
+
+extern "C" unsigned long evma_proxied_bytes (const uintptr_t from)
+{
+ ensure_eventmachine("evma_proxied_bytes");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+ if (ed)
+ return ed->GetProxiedBytes();
+ else
+ return 0;
+}
+
+
+/***************************
+evma_get_last_activity_time
+****************************/
+
+extern "C" uint64_t evma_get_last_activity_time(const uintptr_t from)
+{
+ ensure_eventmachine("evma_get_last_activity_time");
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (from));
+ if (ed)
+ return ed->GetLastActivity();
+ else
+ return 0;
+}
+
+
+/***************************
+evma_get_heartbeat_interval
+****************************/
+
+extern "C" float evma_get_heartbeat_interval()
+{
+ ensure_eventmachine("evma_get_heartbeat_interval");
+ return EventMachine->GetHeartbeatInterval();
+}
+
+
+/***************************
+evma_set_heartbeat_interval
+****************************/
+
+extern "C" int evma_set_heartbeat_interval(float interval)
+{
+ ensure_eventmachine("evma_set_heartbeat_interval");
+ return EventMachine->SetHeartbeatInterval(interval);
+}
+
+
+/**************************
+evma_get_current_loop_time
+***************************/
+
+extern "C" uint64_t evma_get_current_loop_time()
+{
+ ensure_eventmachine("evma_get_current_loop_time");
+ return EventMachine->GetCurrentLoopTime();
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.cpp
new file mode 100644
index 0000000..a469dff
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.cpp
@@ -0,0 +1,2096 @@
+/*****************************************************************************
+
+$Id$
+
+File: ed.cpp
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+
+/********************
+SetSocketNonblocking
+********************/
+
+bool SetSocketNonblocking (SOCKET sd)
+{
+ #ifdef OS_UNIX
+ int val = fcntl (sd, F_GETFL, 0);
+ return (fcntl (sd, F_SETFL, val | O_NONBLOCK) != SOCKET_ERROR) ? true : false;
+ #endif
+
+ #ifdef OS_WIN32
+ #ifdef BUILD_FOR_RUBY
+ // 14Jun09 Ruby provides its own wrappers for ioctlsocket. On 1.8 this is a simple wrapper,
+ // however, 1.9 keeps its own state about the socket.
+ // NOTE: F_GETFL is not supported
+ return (fcntl (sd, F_SETFL, O_NONBLOCK) == 0) ? true : false;
+ #else
+ unsigned long one = 1;
+ return (ioctlsocket (sd, FIONBIO, &one) == 0) ? true : false;
+ #endif
+ #endif
+}
+
+/************
+SetFdCloexec
+************/
+
+#ifdef OS_UNIX
+bool SetFdCloexec (int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ assert (flags >= 0);
+ flags |= FD_CLOEXEC;
+ return (fcntl(fd, F_SETFD, FD_CLOEXEC) == 0) ? true : false;
+}
+#else
+bool SetFdCloexec (int fd UNUSED)
+{
+ return true;
+}
+#endif
+
+/****************************************
+EventableDescriptor::EventableDescriptor
+****************************************/
+
+EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em):
+ bCloseNow (false),
+ bCloseAfterWriting (false),
+ MySocket (sd),
+ bAttached (false),
+ bWatchOnly (false),
+ EventCallback (NULL),
+ bCallbackUnbind (true),
+ UnbindReasonCode (0),
+ ProxyTarget(NULL),
+ ProxiedFrom(NULL),
+ ProxiedBytes(0),
+ MaxOutboundBufSize(0),
+ MyEventMachine (em),
+ PendingConnectTimeout(20000000),
+ InactivityTimeout (0),
+ NextHeartbeat (0),
+ bPaused (false)
+{
+ /* There are three ways to close a socket, all of which should
+ * automatically signal to the event machine that this object
+ * should be removed from the polling scheduler.
+ * First is a hard close, intended for bad errors or possible
+ * security violations. It immediately closes the connection
+ * and puts this object into an error state.
+ * Second is to set bCloseNow, which will cause the event machine
+ * to delete this object (and thus close the connection in our
+ * destructor) the next chance it gets. bCloseNow also inhibits
+ * the writing of new data on the socket (but not necessarily
+ * the reading of new data).
+ * The third way is to set bCloseAfterWriting, which inhibits
+ * the writing of new data and converts to bCloseNow as soon
+ * as everything in the outbound queue has been written.
+ * bCloseAfterWriting is really for use only by protocol handlers
+ * (for example, HTTP writes an HTML page and then closes the
+ * connection). All of the error states we generate internally
+ * cause an immediate close to be scheduled, which may have the
+ * effect of discarding outbound data.
+ */
+
+ if (sd == INVALID_SOCKET)
+ throw std::runtime_error ("bad eventable descriptor");
+ if (MyEventMachine == NULL)
+ throw std::runtime_error ("bad em in eventable descriptor");
+ CreatedAt = MyEventMachine->GetCurrentLoopTime();
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = 0;
+ EpollEvent.data.ptr = this;
+ #endif
+}
+
+
+/*****************************************
+EventableDescriptor::~EventableDescriptor
+*****************************************/
+
+EventableDescriptor::~EventableDescriptor() NO_EXCEPT_FALSE
+{
+ if (NextHeartbeat)
+ MyEventMachine->ClearHeartbeat(NextHeartbeat, this);
+ if (EventCallback && bCallbackUnbind)
+ (*EventCallback)(GetBinding(), EM_CONNECTION_UNBOUND, NULL, UnbindReasonCode);
+ if (ProxiedFrom) {
+ (*EventCallback)(ProxiedFrom->GetBinding(), EM_PROXY_TARGET_UNBOUND, NULL, 0);
+ ProxiedFrom->StopProxy();
+ }
+ MyEventMachine->NumCloseScheduled--;
+ StopProxy();
+ Close();
+}
+
+
+/*************************************
+EventableDescriptor::SetEventCallback
+*************************************/
+
+void EventableDescriptor::SetEventCallback (EMCallback cb)
+{
+ EventCallback = cb;
+}
+
+
+/**************************
+EventableDescriptor::Close
+**************************/
+
+void EventableDescriptor::Close()
+{
+ /* EventMachine relies on the fact that when close(fd)
+ * is called that the fd is removed from any
+ * epoll event queues.
+ *
+ * However, this is not *always* the behavior of close(fd)
+ *
+ * See man 4 epoll Q6/A6 and then consider what happens
+ * when using pipes with eventmachine.
+ * (As is often done when communicating with a subprocess)
+ *
+ * The pipes end up looking like:
+ *
+ * ls -l /proc/<pid>/fd
+ * ...
+ * lr-x------ 1 root root 64 2011-08-19 21:31 3 -> pipe:[940970]
+ * l-wx------ 1 root root 64 2011-08-19 21:31 4 -> pipe:[940970]
+ *
+ * This meets the critera from man 4 epoll Q6/A4 for not
+ * removing fds from epoll event queues until all fds
+ * that reference the underlying file have been removed.
+ *
+ * If the EventableDescriptor associated with fd 3 is deleted,
+ * its dtor will call EventableDescriptor::Close(),
+ * which will call ::close(int fd).
+ *
+ * However, unless the EventableDescriptor associated with fd 4 is
+ * also deleted before the next call to epoll_wait, events may fire
+ * for fd 3 that were registered with an already deleted
+ * EventableDescriptor.
+ *
+ * Therefore, it is necessary to notify EventMachine that
+ * the fd associated with this EventableDescriptor is
+ * closing.
+ *
+ * EventMachine also never closes fds for STDIN, STDOUT and
+ * STDERR (0, 1 & 2)
+ */
+
+ // Close the socket right now. Intended for emergencies.
+ if (MySocket != INVALID_SOCKET) {
+ MyEventMachine->Deregister (this);
+
+ // Do not close STDIN, STDOUT, STDERR
+ if (MySocket > 2 && !bAttached) {
+ shutdown (MySocket, 1);
+ close (MySocket);
+ }
+
+ MySocket = INVALID_SOCKET;
+ }
+}
+
+
+/*********************************
+EventableDescriptor::ShouldDelete
+*********************************/
+
+bool EventableDescriptor::ShouldDelete()
+{
+ /* For use by a socket manager, which needs to know if this object
+ * should be removed from scheduling events and deleted.
+ * Has an immediate close been scheduled, or are we already closed?
+ * If either of these are the case, return true. In theory, the manager will
+ * then delete us, which in turn will make sure the socket is closed.
+ * Note, if bCloseAfterWriting is true, we check a virtual method to see
+ * if there is outbound data to write, and only request a close if there is none.
+ */
+
+ return ((MySocket == INVALID_SOCKET) || bCloseNow || (bCloseAfterWriting && (GetOutboundDataSize() <= 0)));
+}
+
+
+/**********************************
+EventableDescriptor::ScheduleClose
+**********************************/
+
+void EventableDescriptor::ScheduleClose (bool after_writing)
+{
+ if (IsCloseScheduled()) {
+ if (!after_writing) {
+ // If closing has become more urgent, then upgrade the scheduled
+ // after_writing close to one NOW.
+ bCloseNow = true;
+ }
+ return;
+ }
+ MyEventMachine->NumCloseScheduled++;
+ // KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled.
+ if (after_writing)
+ bCloseAfterWriting = true;
+ else
+ bCloseNow = true;
+}
+
+
+/*************************************
+EventableDescriptor::IsCloseScheduled
+*************************************/
+
+bool EventableDescriptor::IsCloseScheduled()
+{
+ // KEEP THIS SYNCHRONIZED WITH ::ScheduleClose.
+ return (bCloseNow || bCloseAfterWriting);
+}
+
+
+/*******************************
+EventableDescriptor::StartProxy
+*******************************/
+
+void EventableDescriptor::StartProxy(const uintptr_t to, const unsigned long bufsize, const unsigned long length)
+{
+ EventableDescriptor *ed = dynamic_cast <EventableDescriptor*> (Bindable_t::GetObject (to));
+ if (ed) {
+ StopProxy();
+ ProxyTarget = ed;
+ BytesToProxy = length;
+ ProxiedBytes = 0;
+ ed->SetProxiedFrom(this, bufsize);
+ return;
+ }
+ throw std::runtime_error ("Tried to proxy to an invalid descriptor");
+}
+
+
+/******************************
+EventableDescriptor::StopProxy
+******************************/
+
+void EventableDescriptor::StopProxy()
+{
+ if (ProxyTarget) {
+ ProxyTarget->SetProxiedFrom(NULL, 0);
+ ProxyTarget = NULL;
+ }
+}
+
+
+/***********************************
+EventableDescriptor::SetProxiedFrom
+***********************************/
+
+void EventableDescriptor::SetProxiedFrom(EventableDescriptor *from, const unsigned long bufsize)
+{
+ if (from != NULL && ProxiedFrom != NULL)
+ throw std::runtime_error ("Tried to proxy to a busy target");
+
+ ProxiedFrom = from;
+ MaxOutboundBufSize = bufsize;
+}
+
+
+/********************************************
+EventableDescriptor::_GenericInboundDispatch
+********************************************/
+
+void EventableDescriptor::_GenericInboundDispatch(const char *buf, unsigned long size)
+{
+ assert(EventCallback);
+
+ if (ProxyTarget) {
+ if (BytesToProxy > 0) {
+ unsigned long proxied = std::min(BytesToProxy, size);
+ ProxyTarget->SendOutboundData(buf, proxied);
+ ProxiedBytes += (unsigned long) proxied;
+ BytesToProxy -= proxied;
+ if (BytesToProxy == 0) {
+ StopProxy();
+ (*EventCallback)(GetBinding(), EM_PROXY_COMPLETED, NULL, 0);
+ if (proxied < size) {
+ (*EventCallback)(GetBinding(), EM_CONNECTION_READ, buf + proxied, size - proxied);
+ }
+ }
+ } else {
+ ProxyTarget->SendOutboundData(buf, size);
+ ProxiedBytes += size;
+ }
+ } else {
+ (*EventCallback)(GetBinding(), EM_CONNECTION_READ, buf, size);
+ }
+}
+
+
+/*********************************
+EventableDescriptor::_GenericGetPeername
+*********************************/
+
+bool EventableDescriptor::_GenericGetPeername (struct sockaddr *s, socklen_t *len)
+{
+ if (!s)
+ return false;
+
+ int gp = getpeername (GetSocket(), s, len);
+ if (gp == -1) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "unable to get peer name: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+
+ return true;
+}
+
+/*********************************
+EventableDescriptor::_GenericGetSockname
+*********************************/
+
+bool EventableDescriptor::_GenericGetSockname (struct sockaddr *s, socklen_t *len)
+{
+ if (!s)
+ return false;
+
+ int gp = getsockname (GetSocket(), s, len);
+ if (gp == -1) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "unable to get sock name: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+
+ return true;
+}
+
+
+/*********************************************
+EventableDescriptor::GetPendingConnectTimeout
+*********************************************/
+
+uint64_t EventableDescriptor::GetPendingConnectTimeout()
+{
+ return PendingConnectTimeout / 1000;
+}
+
+
+/*********************************************
+EventableDescriptor::SetPendingConnectTimeout
+*********************************************/
+
+int EventableDescriptor::SetPendingConnectTimeout (uint64_t value)
+{
+ if (value > 0) {
+ PendingConnectTimeout = value * 1000;
+ MyEventMachine->QueueHeartbeat(this);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*************************************
+EventableDescriptor::GetNextHeartbeat
+*************************************/
+
+uint64_t EventableDescriptor::GetNextHeartbeat()
+{
+ if (NextHeartbeat)
+ MyEventMachine->ClearHeartbeat(NextHeartbeat, this);
+
+ NextHeartbeat = 0;
+
+ if (!ShouldDelete()) {
+ uint64_t time_til_next = InactivityTimeout;
+ if (IsConnectPending()) {
+ if (time_til_next == 0 || PendingConnectTimeout < time_til_next)
+ time_til_next = PendingConnectTimeout;
+ }
+ if (time_til_next == 0)
+ return 0;
+ NextHeartbeat = time_til_next + MyEventMachine->GetRealTime();
+ }
+
+ return NextHeartbeat;
+}
+
+
+/******************************************
+ConnectionDescriptor::ConnectionDescriptor
+******************************************/
+
+ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em):
+ EventableDescriptor (sd, em),
+ bConnectPending (false),
+ bNotifyReadable (false),
+ bNotifyWritable (false),
+ bReadAttemptedAfterClose (false),
+ bWriteAttemptedAfterClose (false),
+ OutboundDataSize (0),
+ #ifdef WITH_SSL
+ SslBox (NULL),
+ bHandshakeSignaled (false),
+ bSslVerifyPeer (false),
+ bSslPeerAccepted(false),
+ #endif
+ #ifdef HAVE_KQUEUE
+ bGotExtraKqueueEvent(false),
+ #endif
+ bIsServer (false)
+{
+ // 22Jan09: Moved ArmKqueueWriter into SetConnectPending() to fix assertion failure in _WriteOutboundData()
+ // 5May09: Moved EPOLLOUT into SetConnectPending() so it doesn't happen for attached read pipes
+}
+
+
+/*******************************************
+ConnectionDescriptor::~ConnectionDescriptor
+*******************************************/
+
+ConnectionDescriptor::~ConnectionDescriptor()
+{
+ // Run down any stranded outbound data.
+ for (size_t i=0; i < OutboundPages.size(); i++)
+ OutboundPages[i].Free();
+
+ #ifdef WITH_SSL
+ if (SslBox)
+ delete SslBox;
+ #endif
+}
+
+
+/***********************************
+ConnectionDescriptor::_UpdateEvents
+************************************/
+
+void ConnectionDescriptor::_UpdateEvents()
+{
+ _UpdateEvents(true, true);
+}
+
+void ConnectionDescriptor::_UpdateEvents(bool read, bool write)
+{
+ if (MySocket == INVALID_SOCKET)
+ return;
+
+ if (!read && !write)
+ return;
+
+ #ifdef HAVE_EPOLL
+ unsigned int old = EpollEvent.events;
+
+ if (read) {
+ if (SelectForRead())
+ EpollEvent.events |= EPOLLIN;
+ else
+ EpollEvent.events &= ~EPOLLIN;
+ }
+
+ if (write) {
+ if (SelectForWrite())
+ EpollEvent.events |= EPOLLOUT;
+ else
+ EpollEvent.events &= ~EPOLLOUT;
+ }
+
+ if (old != EpollEvent.events)
+ MyEventMachine->Modify (this);
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (read && SelectForRead())
+ MyEventMachine->ArmKqueueReader (this);
+ bKqueueArmWrite = SelectForWrite();
+ if (write && bKqueueArmWrite)
+ MyEventMachine->Modify (this);
+ #endif
+}
+
+/***************************************
+ConnectionDescriptor::SetConnectPending
+****************************************/
+
+void ConnectionDescriptor::SetConnectPending(bool f)
+{
+ bConnectPending = f;
+ MyEventMachine->QueueHeartbeat(this);
+ _UpdateEvents();
+}
+
+
+/**********************************
+ConnectionDescriptor::SetAttached
+***********************************/
+
+void ConnectionDescriptor::SetAttached(bool state)
+{
+ bAttached = state;
+}
+
+
+/**********************************
+ConnectionDescriptor::SetWatchOnly
+***********************************/
+
+void ConnectionDescriptor::SetWatchOnly(bool watching)
+{
+ bWatchOnly = watching;
+ _UpdateEvents();
+}
+
+
+/*********************************
+ConnectionDescriptor::HandleError
+*********************************/
+
+void ConnectionDescriptor::HandleError()
+{
+ if (bWatchOnly) {
+ // An EPOLLHUP | EPOLLIN condition will call Read() before HandleError(), in which case the
+ // socket is already detached and invalid, so we don't need to do anything.
+ if (MySocket == INVALID_SOCKET) return;
+
+ // HandleError() is called on WatchOnly descriptors by the epoll reactor
+ // when it gets a EPOLLERR | EPOLLHUP. Usually this would show up as a readable and
+ // writable event on other reactors, so we have to fire those events ourselves.
+ if (bNotifyReadable) Read();
+ if (bNotifyWritable) Write();
+ } else {
+ ScheduleClose (false);
+ }
+}
+
+
+/***********************************
+ConnectionDescriptor::ScheduleClose
+***********************************/
+
+void ConnectionDescriptor::ScheduleClose (bool after_writing)
+{
+ if (bWatchOnly)
+ throw std::runtime_error ("cannot close 'watch only' connections");
+
+ EventableDescriptor::ScheduleClose(after_writing);
+}
+
+
+/***************************************
+ConnectionDescriptor::SetNotifyReadable
+****************************************/
+
+void ConnectionDescriptor::SetNotifyReadable(bool readable)
+{
+ if (!bWatchOnly)
+ throw std::runtime_error ("notify_readable must be on 'watch only' connections");
+
+ bNotifyReadable = readable;
+ _UpdateEvents(true, false);
+}
+
+
+/***************************************
+ConnectionDescriptor::SetNotifyWritable
+****************************************/
+
+void ConnectionDescriptor::SetNotifyWritable(bool writable)
+{
+ if (!bWatchOnly)
+ throw std::runtime_error ("notify_writable must be on 'watch only' connections");
+
+ bNotifyWritable = writable;
+ _UpdateEvents(false, true);
+}
+
+
+/**************************************
+ConnectionDescriptor::SendOutboundData
+**************************************/
+
+int ConnectionDescriptor::SendOutboundData (const char *data, unsigned long length)
+{
+ if (bWatchOnly)
+ throw std::runtime_error ("cannot send data on a 'watch only' connection");
+
+ if (ProxiedFrom && MaxOutboundBufSize && (unsigned int)(GetOutboundDataSize() + length) > MaxOutboundBufSize)
+ ProxiedFrom->Pause();
+
+ #ifdef WITH_SSL
+ if (SslBox) {
+ if (length > 0) {
+ unsigned long writed = 0;
+ char *p = (char*)data;
+
+ while (writed < length) {
+ int to_write = SSLBOX_INPUT_CHUNKSIZE;
+ int remaining = length - writed;
+
+ if (remaining < SSLBOX_INPUT_CHUNKSIZE)
+ to_write = remaining;
+
+ int w = SslBox->PutPlaintext (p, to_write);
+ if (w < 0) {
+ ScheduleClose (false);
+ }else
+ _DispatchCiphertext();
+
+ p += to_write;
+ writed += to_write;
+ }
+ }
+ // TODO: What's the correct return value?
+ return 1; // That's a wild guess, almost certainly wrong.
+ }
+ else
+ #endif
+ return _SendRawOutboundData (data, length);
+}
+
+
+
+/******************************************
+ConnectionDescriptor::_SendRawOutboundData
+******************************************/
+
+int ConnectionDescriptor::_SendRawOutboundData (const char *data, unsigned long length)
+{
+ /* This internal method is called to schedule bytes that
+ * will be sent out to the remote peer.
+ * It's not directly accessed by the caller, who hits ::SendOutboundData,
+ * which may or may not filter or encrypt the caller's data before
+ * sending it here.
+ */
+
+ // Highly naive and incomplete implementation.
+ // There's no throttle for runaways (which should abort only this connection
+ // and not the whole process), and no coalescing of small pages.
+ // (Well, not so bad, small pages are coalesced in ::Write)
+
+ if (IsCloseScheduled())
+ return 0;
+ // 25Mar10: Ignore 0 length packets as they are not meaningful in TCP (as opposed to UDP)
+ // and can cause the assert(nbytes>0) to fail when OutboundPages has a bunch of 0 length pages.
+ if (length == 0)
+ return 0;
+
+ if (!data && (length > 0))
+ throw std::runtime_error ("bad outbound data");
+ char *buffer = (char *) malloc (length + 1);
+ if (!buffer)
+ throw std::runtime_error ("no allocation for outbound data");
+
+ memcpy (buffer, data, length);
+ buffer [length] = 0;
+ OutboundPages.push_back (OutboundPage (buffer, length));
+ OutboundDataSize += length;
+
+ _UpdateEvents(false, true);
+
+ return length;
+}
+
+
+
+/***********************************
+ConnectionDescriptor::SelectForRead
+***********************************/
+
+bool ConnectionDescriptor::SelectForRead()
+{
+ /* A connection descriptor is always scheduled for read,
+ * UNLESS it's in a pending-connect state.
+ * On Linux, unlike Unix, a nonblocking socket on which
+ * connect has been called, does NOT necessarily select
+ * both readable and writable in case of error.
+ * The socket will select writable when the disposition
+ * of the connect is known. On the other hand, a socket
+ * which successfully connects and selects writable may
+ * indeed have some data available on it, so it will
+ * select readable in that case, violating expectations!
+ * So we will not poll for readability until the socket
+ * is known to be in a connected state.
+ */
+
+ if (bPaused)
+ return false;
+ else if (bConnectPending)
+ return false;
+ else if (bWatchOnly)
+ return bNotifyReadable ? true : false;
+ else
+ return true;
+}
+
+
+/************************************
+ConnectionDescriptor::SelectForWrite
+************************************/
+
+bool ConnectionDescriptor::SelectForWrite()
+{
+ /* Cf the notes under SelectForRead.
+ * In a pending-connect state, we ALWAYS select for writable.
+ * In a normal state, we only select for writable when we
+ * have outgoing data to send.
+ */
+
+ if (bPaused)
+ return false;
+ else if (bConnectPending)
+ return true;
+ else if (bWatchOnly)
+ return bNotifyWritable ? true : false;
+ else
+ return (GetOutboundDataSize() > 0);
+}
+
+/***************************
+ConnectionDescriptor::Pause
+***************************/
+
+bool ConnectionDescriptor::Pause()
+{
+ if (bWatchOnly)
+ throw std::runtime_error ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+
+ bool old = bPaused;
+ bPaused = true;
+ _UpdateEvents();
+ return old == false;
+}
+
+/****************************
+ConnectionDescriptor::Resume
+****************************/
+
+bool ConnectionDescriptor::Resume()
+{
+ if (bWatchOnly)
+ throw std::runtime_error ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+
+ bool old = bPaused;
+ bPaused = false;
+ _UpdateEvents();
+ return old == true;
+}
+
+/**************************
+ConnectionDescriptor::Read
+**************************/
+
+void ConnectionDescriptor::Read()
+{
+ /* Read and dispatch data on a socket that has selected readable.
+ * It's theoretically possible to get and dispatch incoming data on
+ * a socket that has already been scheduled for closing or close-after-writing.
+ * In those cases, we'll leave it up the to protocol handler to "do the
+ * right thing" (which probably means to ignore the incoming data).
+ *
+ * 22Aug06: Chris Ochs reports that on FreeBSD, it's possible to come
+ * here with the socket already closed, after the process receives
+ * a ctrl-C signal (not sure if that's TERM or INT on BSD). The application
+ * was one in which network connections were doing a lot of interleaved reads
+ * and writes.
+ * Since we always write before reading (in order to keep the outbound queues
+ * as light as possible), I think what happened is that an interrupt caused
+ * the socket to be closed in ConnectionDescriptor::Write. We'll then
+ * come here in the same pass through the main event loop, and won't get
+ * cleaned up until immediately after.
+ * We originally asserted that the socket was valid when we got here.
+ * To deal properly with the possibility that we are closed when we get here,
+ * I removed the assert. HOWEVER, the potential for an infinite loop scares me,
+ * so even though this is really clunky, I added a flag to assert that we never
+ * come here more than once after being closed. (FCianfrocca)
+ */
+
+ SOCKET sd = GetSocket();
+ //assert (sd != INVALID_SOCKET); (original, removed 22Aug06)
+ if (sd == INVALID_SOCKET) {
+ assert (!bReadAttemptedAfterClose);
+ bReadAttemptedAfterClose = true;
+ return;
+ }
+
+ if (bWatchOnly) {
+ if (bNotifyReadable && EventCallback)
+ (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_READABLE, NULL, 0);
+ return;
+ }
+
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+
+ int total_bytes_read = 0;
+ char readbuffer [16 * 1024 + 1];
+
+ for (int i=0; i < 10; i++) {
+ // Don't read just one buffer and then move on. This is faster
+ // if there is a lot of incoming.
+ // But don't read indefinitely. Give other sockets a chance to run.
+ // NOTICE, we're reading one less than the buffer size.
+ // That's so we can put a guard byte at the end of what we send
+ // to user code.
+
+
+ int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
+#ifdef OS_WIN32
+ int e = WSAGetLastError();
+#else
+ int e = errno;
+#endif
+ //cerr << "<R:" << r << ">";
+
+ if (r > 0) {
+ total_bytes_read += r;
+
+ // Add a null-terminator at the the end of the buffer
+ // that we will send to the callback.
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+ // to be able to depend on this behavior, so they will have
+ // the option to do some things faster. Additionally it's
+ // a security guard against buffer overflows.
+ readbuffer [r] = 0;
+ _DispatchInboundData (readbuffer, r);
+ if (bPaused)
+ break;
+ }
+ else if (r == 0) {
+ break;
+ }
+ else {
+ #ifdef OS_UNIX
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EAGAIN) && (e != EINTR)) {
+ #endif
+ #ifdef OS_WIN32
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
+ #endif
+ // 26Mar11: Previously, all read errors were assumed to be EWOULDBLOCK and ignored.
+ // Now, instead, we call Close() on errors like ECONNRESET and ENOTCONN.
+ UnbindReasonCode = e;
+ Close();
+ break;
+ } else {
+ // Basically a would-block, meaning we've read everything there is to read.
+ break;
+ }
+ }
+
+ }
+
+
+ if (total_bytes_read == 0) {
+ // If we read no data on a socket that selected readable,
+ // it generally means the other end closed the connection gracefully.
+ ScheduleClose (false);
+ //bCloseNow = true;
+ }
+
+}
+
+
+
+/******************************************
+ConnectionDescriptor::_DispatchInboundData
+******************************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size)
+{
+ if (SslBox) {
+ SslBox->PutCiphertext (buffer, size);
+
+ int s;
+ char B [2048];
+ while ((s = SslBox->GetPlaintext (B, sizeof(B) - 1)) > 0) {
+ _CheckHandshakeStatus();
+ B [s] = 0;
+ _GenericInboundDispatch(B, s);
+ }
+
+ // If our SSL handshake had a problem, shut down the connection.
+ if (s == -2) {
+ #ifndef EPROTO // OpenBSD does not have EPROTO
+ #define EPROTO EINTR
+ #endif
+ #ifdef OS_UNIX
+ UnbindReasonCode = EPROTO;
+ #endif
+ #ifdef OS_WIN32
+ UnbindReasonCode = WSAECONNABORTED;
+ #endif
+ ScheduleClose(false);
+ return;
+ }
+
+ _CheckHandshakeStatus();
+ _DispatchCiphertext();
+ }
+ else {
+ _GenericInboundDispatch(buffer, size);
+ }
+}
+#else
+void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size)
+{
+ _GenericInboundDispatch(buffer, size);
+}
+#endif
+
+
+
+/*******************************************
+ConnectionDescriptor::_CheckHandshakeStatus
+*******************************************/
+
+void ConnectionDescriptor::_CheckHandshakeStatus()
+{
+ #ifdef WITH_SSL
+ if (SslBox && (!bHandshakeSignaled) && SslBox->IsHandshakeCompleted()) {
+ bHandshakeSignaled = true;
+ if (EventCallback)
+ (*EventCallback)(GetBinding(), EM_SSL_HANDSHAKE_COMPLETED, NULL, 0);
+ }
+ #endif
+}
+
+
+
+/***************************
+ConnectionDescriptor::Write
+***************************/
+
+void ConnectionDescriptor::Write()
+{
+ /* A socket which is in a pending-connect state will select
+ * writable when the disposition of the connect is known.
+ * At that point, check to be sure there are no errors,
+ * and if none, then promote the socket out of the pending
+ * state.
+ * TODO: I haven't figured out how Windows signals errors on
+ * unconnected sockets. Maybe it does the untraditional but
+ * logical thing and makes the socket selectable for error.
+ * If so, it's unsupported here for the time being, and connect
+ * errors will have to be caught by the timeout mechanism.
+ */
+
+ if (bConnectPending) {
+ int error;
+ socklen_t len;
+ len = sizeof(error);
+ #ifdef OS_UNIX
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
+ #endif
+ #ifdef OS_WIN32
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+ #endif
+ if ((o == 0) && (error == 0)) {
+ if (EventCallback)
+ (*EventCallback)(GetBinding(), EM_CONNECTION_COMPLETED, "", 0);
+
+ // 5May09: Moved epoll/kqueue read/write arming into SetConnectPending, so it can be called
+ // from EventMachine_t::AttachFD as well.
+ SetConnectPending (false);
+ }
+ else {
+ if (o == 0)
+ UnbindReasonCode = error;
+ ScheduleClose (false);
+ //bCloseNow = true;
+ }
+ }
+ else {
+
+ if (bNotifyWritable) {
+ if (EventCallback)
+ (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0);
+
+ _UpdateEvents(false, true);
+ return;
+ }
+
+ assert(!bWatchOnly);
+
+ /* 5May09: Kqueue bugs on OSX cause one extra writable event to fire even though we're using
+ EV_ONESHOT. We ignore this extra event once, but only the first time. If it happens again,
+ we should fall through to the assert(nbytes>0) failure to catch any EM bugs which might cause
+ ::Write to be called in a busy-loop.
+ */
+ #ifdef HAVE_KQUEUE
+ if (MyEventMachine->GetPoller() == Poller_Kqueue) {
+ if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) {
+ bGotExtraKqueueEvent = true;
+ return;
+ } else if (OutboundDataSize > 0) {
+ bGotExtraKqueueEvent = false;
+ }
+ }
+ #endif
+
+ _WriteOutboundData();
+ }
+}
+
+
+/****************************************
+ConnectionDescriptor::_WriteOutboundData
+****************************************/
+
+void ConnectionDescriptor::_WriteOutboundData()
+{
+ /* This is a helper function called by ::Write.
+ * It's possible for a socket to select writable and then no longer
+ * be writable by the time we get around to writing. The kernel might
+ * have used up its available output buffers between the select call
+ * and when we get here. So this condition is not an error.
+ *
+ * 20Jul07, added the same kind of protection against an invalid socket
+ * that is at the top of ::Read. Not entirely how this could happen in
+ * real life (connection-reset from the remote peer, perhaps?), but I'm
+ * doing it to address some reports of crashing under heavy loads.
+ */
+
+ SOCKET sd = GetSocket();
+ //assert (sd != INVALID_SOCKET);
+ if (sd == INVALID_SOCKET) {
+ assert (!bWriteAttemptedAfterClose);
+ bWriteAttemptedAfterClose = true;
+ return;
+ }
+
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+ size_t nbytes = 0;
+
+ #ifdef HAVE_WRITEV
+ int iovcnt = OutboundPages.size();
+ // Max of 16 outbound pages at a time
+ if (iovcnt > 16) iovcnt = 16;
+
+ iovec iov[16];
+
+ for(int i = 0; i < iovcnt; i++){
+ OutboundPage *op = &(OutboundPages[i]);
+ #ifdef CC_SUNWspro
+ // TODO: The void * cast works fine on Solaris 11, but
+ // I don't know at what point that changed from older Solaris.
+ iov[i].iov_base = (char *)(op->Buffer + op->Offset);
+ #else
+ iov[i].iov_base = (void *)(op->Buffer + op->Offset);
+ #endif
+ iov[i].iov_len = op->Length - op->Offset;
+
+ nbytes += iov[i].iov_len;
+ }
+ #else
+ char output_buffer [16 * 1024];
+
+ while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
+ OutboundPage *op = &(OutboundPages[0]);
+ if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
+ nbytes += (op->Length - op->Offset);
+ op->Free();
+ OutboundPages.pop_front();
+ }
+ else {
+ int len = sizeof(output_buffer) - nbytes;
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
+ op->Offset += len;
+ nbytes += len;
+ }
+ }
+ #endif
+
+ // We should never have gotten here if there were no data to write,
+ // so assert that as a sanity check.
+ // Don't bother to make sure nbytes is less than output_buffer because
+ // if it were we probably would have crashed already.
+ assert (nbytes > 0);
+
+ assert (GetSocket() != INVALID_SOCKET);
+ #ifdef HAVE_WRITEV
+ int bytes_written = writev (GetSocket(), iov, iovcnt);
+ #else
+ int bytes_written = write (GetSocket(), output_buffer, nbytes);
+ #endif
+
+ bool err = false;
+#ifdef OS_WIN32
+ int e = WSAGetLastError();
+#else
+ int e = errno;
+#endif
+ if (bytes_written < 0) {
+ err = true;
+ bytes_written = 0;
+ }
+
+ assert (bytes_written >= 0);
+ OutboundDataSize -= bytes_written;
+
+ if (ProxiedFrom && MaxOutboundBufSize && (unsigned int)GetOutboundDataSize() < MaxOutboundBufSize && ProxiedFrom->IsPaused())
+ ProxiedFrom->Resume();
+
+ #ifdef HAVE_WRITEV
+ if (!err) {
+ unsigned int sent = bytes_written;
+ std::deque<OutboundPage>::iterator op = OutboundPages.begin();
+
+ for (int i = 0; i < iovcnt; i++) {
+ if (iov[i].iov_len <= sent) {
+ // Sent this page in full, free it.
+ op->Free();
+ OutboundPages.pop_front();
+
+ sent -= iov[i].iov_len;
+ } else {
+ // Sent part (or none) of this page, increment offset to send the remainder
+ op->Offset += sent;
+ break;
+ }
+
+ // Shouldn't be possible run out of pages before the loop ends
+ assert(op != OutboundPages.end());
+ *op++;
+ }
+ }
+ #else
+ if ((size_t)bytes_written < nbytes) {
+ int len = nbytes - bytes_written;
+ char *buffer = (char*) malloc (len + 1);
+ if (!buffer)
+ throw std::runtime_error ("bad alloc throwing back data");
+ memcpy (buffer, output_buffer + bytes_written, len);
+ buffer [len] = 0;
+ OutboundPages.push_front (OutboundPage (buffer, len));
+ }
+ #endif
+
+ _UpdateEvents(false, true);
+
+ if (err) {
+ #ifdef OS_UNIX
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
+ #endif
+ #ifdef OS_WIN32
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
+ #endif
+ UnbindReasonCode = e;
+ Close();
+ }
+ }
+}
+
+
+/***************************************
+ConnectionDescriptor::ReportErrorStatus
+***************************************/
+
+int ConnectionDescriptor::ReportErrorStatus()
+{
+ if (MySocket == INVALID_SOCKET) {
+ return -1;
+ }
+
+ int error;
+ socklen_t len;
+ len = sizeof(error);
+ #ifdef OS_UNIX
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len);
+ #endif
+ #ifdef OS_WIN32
+ int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len);
+ #endif
+ if ((o == 0) && (error == 0))
+ return 0;
+ else if (o == 0)
+ return error;
+ else
+ return -1;
+}
+
+
+/******************************
+ConnectionDescriptor::StartTls
+******************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::StartTls()
+{
+ if (SslBox)
+ throw std::runtime_error ("SSL/TLS already running on connection");
+
+ SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslFailIfNoPeerCert, SniHostName, CipherList, EcdhCurve, DhParam, Protocols, GetBinding());
+ _DispatchCiphertext();
+
+}
+#else
+void ConnectionDescriptor::StartTls()
+{
+ throw std::runtime_error ("Encryption not available on this event-machine");
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::SetTlsParms
+*********************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols)
+{
+ if (SslBox)
+ throw std::runtime_error ("call SetTlsParms before calling StartTls");
+ if (privkey_filename && *privkey_filename)
+ PrivateKeyFilename = privkey_filename;
+ if (certchain_filename && *certchain_filename)
+ CertChainFilename = certchain_filename;
+ bSslVerifyPeer = verify_peer;
+ bSslFailIfNoPeerCert = fail_if_no_peer_cert;
+
+ if (sni_hostname && *sni_hostname)
+ SniHostName = sni_hostname;
+ if (cipherlist && *cipherlist)
+ CipherList = cipherlist;
+ if (ecdh_curve && *ecdh_curve)
+ EcdhCurve = ecdh_curve;
+ if (dhparam && *dhparam)
+ DhParam = dhparam;
+
+ Protocols = protocols;
+}
+#else
+void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool fail_if_no_peer_cert UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, const char *ecdh_curve UNUSED, const char *dhparam UNUSED, int protocols UNUSED)
+{
+ throw std::runtime_error ("Encryption not available on this event-machine");
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetPeerCert
+*********************************/
+
+#ifdef WITH_SSL
+X509 *ConnectionDescriptor::GetPeerCert()
+{
+ if (!SslBox)
+ throw std::runtime_error ("SSL/TLS not running on this connection");
+ return SslBox->GetPeerCert();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetCipherBits
+*********************************/
+
+#ifdef WITH_SSL
+int ConnectionDescriptor::GetCipherBits()
+{
+ if (!SslBox)
+ throw std::runtime_error ("SSL/TLS not running on this connection");
+ return SslBox->GetCipherBits();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetCipherName
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetCipherName()
+{
+ if (!SslBox)
+ throw std::runtime_error ("SSL/TLS not running on this connection");
+ return SslBox->GetCipherName();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetCipherProtocol
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetCipherProtocol()
+{
+ if (!SslBox)
+ throw std::runtime_error ("SSL/TLS not running on this connection");
+ return SslBox->GetCipherProtocol();
+}
+#endif
+
+
+/*********************************
+ConnectionDescriptor::GetSNIHostname
+*********************************/
+
+#ifdef WITH_SSL
+const char *ConnectionDescriptor::GetSNIHostname()
+{
+ if (!SslBox)
+ throw std::runtime_error ("SSL/TLS not running on this connection");
+ return SslBox->GetSNIHostname();
+}
+#endif
+
+
+/***********************************
+ConnectionDescriptor::VerifySslPeer
+***********************************/
+
+#ifdef WITH_SSL
+bool ConnectionDescriptor::VerifySslPeer(const char *cert)
+{
+ bSslPeerAccepted = false;
+
+ if (EventCallback)
+ (*EventCallback)(GetBinding(), EM_SSL_VERIFY, cert, strlen(cert));
+
+ return bSslPeerAccepted;
+}
+#endif
+
+
+/***********************************
+ConnectionDescriptor::AcceptSslPeer
+***********************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::AcceptSslPeer()
+{
+ bSslPeerAccepted = true;
+}
+#endif
+
+
+/*****************************************
+ConnectionDescriptor::_DispatchCiphertext
+*****************************************/
+
+#ifdef WITH_SSL
+void ConnectionDescriptor::_DispatchCiphertext()
+{
+ assert (SslBox);
+
+
+ char BigBuf [SSLBOX_OUTPUT_CHUNKSIZE];
+ bool did_work;
+
+ do {
+ did_work = false;
+
+ // try to drain ciphertext
+ while (SslBox->CanGetCiphertext()) {
+ int r = SslBox->GetCiphertext (BigBuf, sizeof(BigBuf));
+ assert (r > 0);
+ _SendRawOutboundData (BigBuf, r);
+ did_work = true;
+ }
+
+ // Pump the SslBox, in case it has queued outgoing plaintext
+ // This will return >0 if data was written,
+ // 0 if no data was written, and <0 if there was a fatal error.
+ bool pump;
+ do {
+ pump = false;
+ int w = SslBox->PutPlaintext (NULL, 0);
+ if (w > 0) {
+ did_work = true;
+ pump = true;
+ }
+ else if (w < 0)
+ ScheduleClose (false);
+ } while (pump);
+
+ // try to put plaintext. INCOMPLETE, doesn't belong here?
+ // In SendOutboundData, we're spooling plaintext directly
+ // into SslBox. That may be wrong, we may need to buffer it
+ // up here!
+ /*
+ const char *ptr;
+ int ptr_length;
+ while (OutboundPlaintext.GetPage (&ptr, &ptr_length)) {
+ assert (ptr && (ptr_length > 0));
+ int w = SslMachine.PutPlaintext (ptr, ptr_length);
+ if (w > 0) {
+ OutboundPlaintext.DiscardBytes (w);
+ did_work = true;
+ }
+ else
+ break;
+ }
+ */
+
+ } while (did_work);
+
+}
+#endif
+
+
+
+/*******************************
+ConnectionDescriptor::Heartbeat
+*******************************/
+
+void ConnectionDescriptor::Heartbeat()
+{
+ /* Only allow a certain amount of time to go by while waiting
+ * for a pending connect. If it expires, then kill the socket.
+ * For a connected socket, close it if its inactivity timer
+ * has expired.
+ */
+
+ if (bConnectPending) {
+ if ((MyEventMachine->GetCurrentLoopTime() - CreatedAt) >= PendingConnectTimeout) {
+ UnbindReasonCode = ETIMEDOUT;
+ ScheduleClose (false);
+ //bCloseNow = true;
+ }
+ }
+ else {
+ if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) {
+ UnbindReasonCode = ETIMEDOUT;
+ ScheduleClose (false);
+ //bCloseNow = true;
+ }
+ }
+}
+
+
+/****************************************
+LoopbreakDescriptor::LoopbreakDescriptor
+****************************************/
+
+LoopbreakDescriptor::LoopbreakDescriptor (SOCKET sd, EventMachine_t *parent_em):
+ EventableDescriptor (sd, parent_em)
+{
+ /* This is really bad and ugly. Change someday if possible.
+ * We have to know about an event-machine (probably the one that owns us),
+ * so we can pass newly-created connections to it.
+ */
+
+ bCallbackUnbind = false;
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+ #ifdef HAVE_KQUEUE
+ MyEventMachine->ArmKqueueReader (this);
+ #endif
+}
+
+
+
+
+/*************************
+LoopbreakDescriptor::Read
+*************************/
+
+void LoopbreakDescriptor::Read()
+{
+ // TODO, refactor, this code is probably in the wrong place.
+ assert (MyEventMachine);
+ MyEventMachine->_ReadLoopBreaker();
+}
+
+
+/**************************
+LoopbreakDescriptor::Write
+**************************/
+
+void LoopbreakDescriptor::Write()
+{
+ // Why are we here?
+ throw std::runtime_error ("bad code path in loopbreak");
+}
+
+/**************************************
+AcceptorDescriptor::AcceptorDescriptor
+**************************************/
+
+AcceptorDescriptor::AcceptorDescriptor (SOCKET sd, EventMachine_t *parent_em):
+ EventableDescriptor (sd, parent_em)
+{
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+ #ifdef HAVE_KQUEUE
+ MyEventMachine->ArmKqueueReader (this);
+ #endif
+}
+
+
+/***************************************
+AcceptorDescriptor::~AcceptorDescriptor
+***************************************/
+
+AcceptorDescriptor::~AcceptorDescriptor()
+{
+}
+
+/****************************************
+STATIC: AcceptorDescriptor::StopAcceptor
+****************************************/
+
+void AcceptorDescriptor::StopAcceptor (const uintptr_t binding)
+{
+ // TODO: This is something of a hack, or at least it's a static method of the wrong class.
+ AcceptorDescriptor *ad = dynamic_cast <AcceptorDescriptor*> (Bindable_t::GetObject (binding));
+ if (ad)
+ ad->ScheduleClose (false);
+ else
+ throw std::runtime_error ("failed to close nonexistent acceptor");
+}
+
+
+/************************
+AcceptorDescriptor::Read
+************************/
+
+void AcceptorDescriptor::Read()
+{
+ /* Accept up to a certain number of sockets on the listening connection.
+ * Don't try to accept all that are present, because this would allow a DoS attack
+ * in which no data were ever read or written. We should accept more than one,
+ * if available, to keep the partially accepted sockets from backing up in the kernel.
+ */
+
+ /* Make sure we use non-blocking i/o on the acceptor socket, since we're selecting it
+ * for readability. According to Stevens UNP, it's possible for an acceptor to select readable
+ * and then block when we call accept. For example, the other end resets the connection after
+ * the socket selects readable and before we call accept. The kernel will remove the dead
+ * socket from the accept queue. If the accept queue is now empty, accept will block.
+ */
+
+
+ struct sockaddr_in6 pin;
+ socklen_t addrlen = sizeof (pin);
+ int accept_count = EventMachine_t::GetSimultaneousAcceptCount();
+
+ for (int i=0; i < accept_count; i++) {
+#if defined(HAVE_CONST_SOCK_CLOEXEC) && defined(HAVE_ACCEPT4)
+ SOCKET sd = accept4 (GetSocket(), (struct sockaddr*)&pin, &addrlen, SOCK_CLOEXEC);
+ if (sd == INVALID_SOCKET) {
+ // We may be running in a kernel where
+ // SOCK_CLOEXEC is not supported - fall back:
+ sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+ }
+#else
+ SOCKET sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen);
+#endif
+ if (sd == INVALID_SOCKET) {
+ // This breaks the loop when we've accepted everything on the kernel queue,
+ // up to 10 new connections. But what if the *first* accept fails?
+ // Does that mean anything serious is happening, beyond the situation
+ // described in the note above?
+ break;
+ }
+
+ // Set the newly-accepted socket non-blocking and to close on exec.
+ // On Windows, this may fail because, weirdly, Windows inherits the non-blocking
+ // attribute that we applied to the acceptor socket into the accepted one.
+ if (!SetFdCloexec(sd) || !SetSocketNonblocking (sd)) {
+ //int val = fcntl (sd, F_GETFL, 0);
+ //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) {
+ shutdown (sd, 1);
+ close (sd);
+ continue;
+ }
+
+ // Disable slow-start (Nagle algorithm). Eventually make this configurable.
+ int one = 1;
+ setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
+
+
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, MyEventMachine);
+ if (!cd)
+ throw std::runtime_error ("no newly accepted connection");
+ cd->SetServerMode();
+ if (EventCallback) {
+ (*EventCallback) (GetBinding(), EM_CONNECTION_ACCEPTED, NULL, cd->GetBinding());
+ }
+ #ifdef HAVE_EPOLL
+ cd->GetEpollEvent()->events = 0;
+ if (cd->SelectForRead())
+ cd->GetEpollEvent()->events |= EPOLLIN;
+ if (cd->SelectForWrite())
+ cd->GetEpollEvent()->events |= EPOLLOUT;
+ #endif
+ assert (MyEventMachine);
+ MyEventMachine->Add (cd);
+ #ifdef HAVE_KQUEUE
+ bKqueueArmWrite = cd->SelectForWrite();
+ if (bKqueueArmWrite)
+ MyEventMachine->Modify (cd);
+ if (cd->SelectForRead())
+ MyEventMachine->ArmKqueueReader (cd);
+ #endif
+ }
+
+}
+
+
+/*************************
+AcceptorDescriptor::Write
+*************************/
+
+void AcceptorDescriptor::Write()
+{
+ // Why are we here?
+ throw std::runtime_error ("bad code path in acceptor");
+}
+
+
+/*****************************
+AcceptorDescriptor::Heartbeat
+*****************************/
+
+void AcceptorDescriptor::Heartbeat()
+{
+ // No-op
+}
+
+
+/**************************************
+DatagramDescriptor::DatagramDescriptor
+**************************************/
+
+DatagramDescriptor::DatagramDescriptor (SOCKET sd, EventMachine_t *parent_em):
+ EventableDescriptor (sd, parent_em),
+ OutboundDataSize (0)
+{
+ memset (&ReturnAddress, 0, sizeof(ReturnAddress));
+
+ /* Provisionally added 19Oct07. All datagram sockets support broadcasting.
+ * Until now, sending to a broadcast address would give EACCES (permission denied)
+ * on systems like Linux and BSD that require the SO_BROADCAST socket-option in order
+ * to accept a packet to a broadcast address. Solaris doesn't require it. I think
+ * Windows DOES require it but I'm not sure.
+ *
+ * Ruby does NOT do what we're doing here. In Ruby, you have to explicitly set SO_BROADCAST
+ * on a UDP socket in order to enable broadcasting. The reason for requiring the option
+ * in the first place is so that applications don't send broadcast datagrams by mistake.
+ * I imagine that could happen if a user of an application typed in an address that happened
+ * to be a broadcast address on that particular subnet.
+ *
+ * This is provisional because someone may eventually come up with a good reason not to
+ * do it for all UDP sockets. If that happens, then we'll need to add a usercode-level API
+ * to set the socket option, just like Ruby does. AND WE'LL ALSO BREAK CODE THAT DOESN'T
+ * EXPLICITLY SET THE OPTION.
+ */
+
+ int oval = 1;
+ setsockopt (GetSocket(), SOL_SOCKET, SO_BROADCAST, (char*)&oval, sizeof(oval));
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+ #ifdef HAVE_KQUEUE
+ MyEventMachine->ArmKqueueReader (this);
+ #endif
+}
+
+
+/***************************************
+DatagramDescriptor::~DatagramDescriptor
+***************************************/
+
+DatagramDescriptor::~DatagramDescriptor()
+{
+ // Run down any stranded outbound data.
+ for (size_t i=0; i < OutboundPages.size(); i++)
+ OutboundPages[i].Free();
+}
+
+
+/*****************************
+DatagramDescriptor::Heartbeat
+*****************************/
+
+void DatagramDescriptor::Heartbeat()
+{
+ // Close it if its inactivity timer has expired.
+
+ if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout))
+ ScheduleClose (false);
+ //bCloseNow = true;
+}
+
+
+/************************
+DatagramDescriptor::Read
+************************/
+
+void DatagramDescriptor::Read()
+{
+ SOCKET sd = GetSocket();
+ assert (sd != INVALID_SOCKET);
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+
+ // This is an extremely large read buffer.
+ // In many cases you wouldn't expect to get any more than 4K.
+ char readbuffer [16 * 1024];
+
+ for (int i=0; i < 10; i++) {
+ // Don't read just one buffer and then move on. This is faster
+ // if there is a lot of incoming.
+ // But don't read indefinitely. Give other sockets a chance to run.
+ // NOTICE, we're reading one less than the buffer size.
+ // That's so we can put a guard byte at the end of what we send
+ // to user code.
+
+ struct sockaddr_in6 sin;
+ socklen_t slen = sizeof (sin);
+ memset (&sin, 0, slen);
+
+ int r = recvfrom (sd, readbuffer, sizeof(readbuffer) - 1, 0, (struct sockaddr*)&sin, &slen);
+ //cerr << "<R:" << r << ">";
+
+ // In UDP, a zero-length packet is perfectly legal.
+ if (r >= 0) {
+
+ // Add a null-terminator at the the end of the buffer
+ // that we will send to the callback.
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+ // to be able to depend on this behavior, so they will have
+ // the option to do some things faster. Additionally it's
+ // a security guard against buffer overflows.
+ readbuffer [r] = 0;
+
+
+ // Set up a "temporary" return address so that callers can "reply" to us
+ // from within the callback we are about to invoke. That means that ordinary
+ // calls to "send_data_to_connection" (which is of course misnamed in this
+ // case) will result in packets being sent back to the same place that sent
+ // us this one.
+ // There is a different call (evma_send_datagram) for cases where the caller
+ // actually wants to send a packet somewhere else.
+
+ memset (&ReturnAddress, 0, sizeof(ReturnAddress));
+ memcpy (&ReturnAddress, &sin, slen);
+
+ _GenericInboundDispatch(readbuffer, r);
+
+ }
+ else {
+ // Basically a would-block, meaning we've read everything there is to read.
+ break;
+ }
+
+ }
+
+
+}
+
+
+/*************************
+DatagramDescriptor::Write
+*************************/
+
+void DatagramDescriptor::Write()
+{
+ /* It's possible for a socket to select writable and then no longer
+ * be writable by the time we get around to writing. The kernel might
+ * have used up its available output buffers between the select call
+ * and when we get here. So this condition is not an error.
+ * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData,
+ * but differs in the that the outbound data pages (received from the
+ * user) are _message-structured._ That is, we send each of them out
+ * one message at a time.
+ * TODO, we are currently suppressing the EMSGSIZE error!!!
+ */
+
+ SOCKET sd = GetSocket();
+ assert (sd != INVALID_SOCKET);
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+
+ assert (OutboundPages.size() > 0);
+
+ // Send out up to 10 packets, then cycle the machine.
+ for (int i = 0; i < 10; i++) {
+ if (OutboundPages.size() <= 0)
+ break;
+ OutboundPage *op = &(OutboundPages[0]);
+
+ // The nasty cast to (char*) is needed because Windows is brain-dead.
+ int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From),
+ (op->From.sin6_family == AF_INET6 ? sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in)));
+#ifdef OS_WIN32
+ int e = WSAGetLastError();
+#else
+ int e = errno;
+#endif
+
+ OutboundDataSize -= op->Length;
+ op->Free();
+ OutboundPages.pop_front();
+
+ if (s == SOCKET_ERROR) {
+ #ifdef OS_UNIX
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) {
+ #endif
+ #ifdef OS_WIN32
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) {
+ #endif
+ UnbindReasonCode = e;
+ Close();
+ break;
+ }
+ }
+ }
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ if (SelectForWrite())
+ EpollEvent.events |= EPOLLOUT;
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+ #ifdef HAVE_KQUEUE
+ bKqueueArmWrite = SelectForWrite();
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+}
+
+
+/**********************************
+DatagramDescriptor::SelectForWrite
+**********************************/
+
+bool DatagramDescriptor::SelectForWrite()
+{
+ /* Changed 15Nov07, per bug report by Mark Zvillius.
+ * The outbound data size will be zero if there are zero-length outbound packets,
+ * so we now select writable in case the outbound page buffer is not empty.
+ * Note that the superclass ShouldDelete method still checks for outbound data size,
+ * which may be wrong.
+ */
+ //return (GetOutboundDataSize() > 0); (Original)
+ return (OutboundPages.size() > 0);
+}
+
+
+/************************************
+DatagramDescriptor::SendOutboundData
+************************************/
+
+int DatagramDescriptor::SendOutboundData (const char *data, unsigned long length)
+{
+ // This is almost an exact clone of ConnectionDescriptor::_SendRawOutboundData.
+ // That means most of it could be factored to a common ancestor. Note that
+ // empty datagrams are meaningful, which isn't the case for TCP streams.
+
+ if (IsCloseScheduled())
+ return 0;
+
+ if (!data && (length > 0))
+ throw std::runtime_error ("bad outbound data");
+ char *buffer = (char *) malloc (length + 1);
+ if (!buffer)
+ throw std::runtime_error ("no allocation for outbound data");
+ memcpy (buffer, data, length);
+ buffer [length] = 0;
+ OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress));
+ OutboundDataSize += length;
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+ #ifdef HAVE_KQUEUE
+ bKqueueArmWrite = true;
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+
+ return length;
+}
+
+
+/****************************************
+DatagramDescriptor::SendOutboundDatagram
+****************************************/
+
+int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long length, const char *address, int port)
+{
+ // This is an exact clone of ConnectionDescriptor::SendOutboundData.
+ // That means it needs to move to a common ancestor.
+ // TODO: Refactor this so there's no overlap with SendOutboundData.
+
+ if (IsCloseScheduled())
+ //if (bCloseNow || bCloseAfterWriting)
+ return 0;
+
+ if (!address || !*address || !port)
+ return 0;
+
+ struct sockaddr_in6 addr_here;
+ size_t addr_here_len = sizeof addr_here;
+ if (0 != EventMachine_t::name2address (address, port, SOCK_DGRAM, (struct sockaddr *)&addr_here, &addr_here_len))
+ return -1;
+
+ if (!data && (length > 0))
+ throw std::runtime_error ("bad outbound data");
+ char *buffer = (char *) malloc (length + 1);
+ if (!buffer)
+ throw std::runtime_error ("no allocation for outbound data");
+ memcpy (buffer, data, length);
+ buffer [length] = 0;
+ OutboundPages.push_back (OutboundPage (buffer, length, addr_here));
+ OutboundDataSize += length;
+
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+ #ifdef HAVE_KQUEUE
+ bKqueueArmWrite = true;
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+
+ return length;
+}
+
+
+/**********************************************
+ConnectionDescriptor::GetCommInactivityTimeout
+**********************************************/
+
+uint64_t ConnectionDescriptor::GetCommInactivityTimeout()
+{
+ return InactivityTimeout / 1000;
+}
+
+
+/**********************************************
+ConnectionDescriptor::SetCommInactivityTimeout
+**********************************************/
+
+int ConnectionDescriptor::SetCommInactivityTimeout (uint64_t value)
+{
+ InactivityTimeout = value * 1000;
+ MyEventMachine->QueueHeartbeat(this);
+ return 1;
+}
+
+/*******************************
+DatagramDescriptor::GetPeername
+*******************************/
+
+bool DatagramDescriptor::GetPeername (struct sockaddr *s, socklen_t *len)
+{
+ bool ok = false;
+ if (s) {
+ *len = sizeof(ReturnAddress);
+ memset (s, 0, sizeof(ReturnAddress));
+ memcpy (s, &ReturnAddress, sizeof(ReturnAddress));
+ ok = true;
+ }
+ return ok;
+}
+
+
+/********************************************
+DatagramDescriptor::GetCommInactivityTimeout
+********************************************/
+
+uint64_t DatagramDescriptor::GetCommInactivityTimeout()
+{
+ return InactivityTimeout / 1000;
+}
+
+/********************************************
+DatagramDescriptor::SetCommInactivityTimeout
+********************************************/
+
+int DatagramDescriptor::SetCommInactivityTimeout (uint64_t value)
+{
+ if (value > 0) {
+ InactivityTimeout = value * 1000;
+ MyEventMachine->QueueHeartbeat(this);
+ return 1;
+ }
+ return 0;
+}
+
+
+/************************************
+InotifyDescriptor::InotifyDescriptor
+*************************************/
+
+InotifyDescriptor::InotifyDescriptor (EventMachine_t *em):
+ EventableDescriptor(0, em)
+{
+ bCallbackUnbind = false;
+
+ #ifndef HAVE_INOTIFY
+ throw std::runtime_error("no inotify support on this system");
+ #else
+
+ int fd = inotify_init();
+ if (fd == -1) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "unable to create inotify descriptor: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+
+ MySocket = fd;
+ SetSocketNonblocking(MySocket);
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+
+ #endif
+}
+
+
+/*************************************
+InotifyDescriptor::~InotifyDescriptor
+**************************************/
+
+InotifyDescriptor::~InotifyDescriptor()
+{
+ close(MySocket);
+ MySocket = INVALID_SOCKET;
+}
+
+/***********************
+InotifyDescriptor::Read
+************************/
+
+void InotifyDescriptor::Read()
+{
+ assert (MyEventMachine);
+ MyEventMachine->_ReadInotifyEvents();
+}
+
+
+/************************
+InotifyDescriptor::Write
+*************************/
+
+void InotifyDescriptor::Write()
+{
+ throw std::runtime_error("bad code path in inotify");
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.h
new file mode 100644
index 0000000..4d7f7d4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ed.h
@@ -0,0 +1,457 @@
+/*****************************************************************************
+
+$Id$
+
+File: ed.h
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+#ifndef __EventableDescriptor__H_
+#define __EventableDescriptor__H_
+
+
+class EventMachine_t; // forward reference
+#ifdef WITH_SSL
+class SslBox_t; // forward reference
+#endif
+
+bool SetSocketNonblocking (SOCKET);
+bool SetFdCloexec (int);
+
+/*************************
+class EventableDescriptor
+*************************/
+
+class EventableDescriptor: public Bindable_t
+{
+ public:
+ EventableDescriptor (SOCKET, EventMachine_t*);
+ virtual ~EventableDescriptor() NO_EXCEPT_FALSE;
+
+ SOCKET GetSocket() {return MySocket;}
+ void SetSocketInvalid() { MySocket = INVALID_SOCKET; }
+ void Close();
+
+ virtual void Read() = 0;
+ virtual void Write() = 0;
+ virtual void Heartbeat() = 0;
+
+ // These methods tell us whether the descriptor
+ // should be selected or polled for read/write.
+ virtual bool SelectForRead() = 0;
+ virtual bool SelectForWrite() = 0;
+
+ // are we scheduled for a close, or in an error state, or already closed?
+ bool ShouldDelete();
+ // Do we have any data to write? This is used by ShouldDelete.
+ virtual int GetOutboundDataSize() {return 0;}
+ virtual bool IsWatchOnly(){ return bWatchOnly; }
+
+ virtual void ScheduleClose (bool after_writing);
+ bool IsCloseScheduled();
+ virtual void HandleError(){ ScheduleClose (false); }
+
+ void SetEventCallback (EMCallback);
+
+ virtual bool GetPeername (struct sockaddr*, socklen_t*) = 0;
+ virtual bool GetSockname (struct sockaddr*, socklen_t*) = 0;
+ virtual bool GetSubprocessPid (pid_t*) {return false;}
+
+ virtual void StartTls() {}
+ virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {}
+
+ #ifdef WITH_SSL
+ virtual X509 *GetPeerCert() {return NULL;}
+ virtual int GetCipherBits() {return -1;}
+ virtual const char *GetCipherName() {return NULL;}
+ virtual const char *GetCipherProtocol() {return NULL;}
+ virtual const char *GetSNIHostname() {return NULL;}
+ #endif
+
+ virtual uint64_t GetCommInactivityTimeout() {return 0;}
+ virtual int SetCommInactivityTimeout (uint64_t) {return 0;}
+ uint64_t GetPendingConnectTimeout();
+ int SetPendingConnectTimeout (uint64_t value);
+ uint64_t GetLastActivity() { return LastActivity; }
+
+ #ifdef HAVE_EPOLL
+ struct epoll_event *GetEpollEvent() { return &EpollEvent; }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ bool GetKqueueArmWrite() { return bKqueueArmWrite; }
+ #endif
+
+ virtual void StartProxy(const uintptr_t, const unsigned long, const unsigned long);
+ virtual void StopProxy();
+ virtual unsigned long GetProxiedBytes(){ return ProxiedBytes; };
+ virtual void SetProxiedFrom(EventableDescriptor*, const unsigned long);
+ virtual int SendOutboundData(const char*,unsigned long){ return -1; }
+ virtual bool IsPaused(){ return bPaused; }
+ virtual bool Pause(){ bPaused = true; return bPaused; }
+ virtual bool Resume(){ bPaused = false; return bPaused; }
+
+ void SetUnbindReasonCode(int code){ UnbindReasonCode = code; }
+ virtual int ReportErrorStatus(){ return 0; }
+ virtual bool IsConnectPending(){ return false; }
+ virtual uint64_t GetNextHeartbeat();
+
+ private:
+ bool bCloseNow;
+ bool bCloseAfterWriting;
+
+ protected:
+ SOCKET MySocket;
+ bool bAttached;
+ bool bWatchOnly;
+
+ EMCallback EventCallback;
+ void _GenericInboundDispatch (const char *buffer, unsigned long size);
+ bool _GenericGetPeername (struct sockaddr*, socklen_t*);
+ bool _GenericGetSockname (struct sockaddr*, socklen_t*);
+
+ uint64_t CreatedAt;
+ bool bCallbackUnbind;
+ int UnbindReasonCode;
+
+ unsigned long BytesToProxy;
+ EventableDescriptor *ProxyTarget;
+ EventableDescriptor *ProxiedFrom;
+ unsigned long ProxiedBytes;
+
+ unsigned long MaxOutboundBufSize;
+
+ #ifdef HAVE_EPOLL
+ struct epoll_event EpollEvent;
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ bool bKqueueArmWrite;
+ #endif
+
+ EventMachine_t *MyEventMachine;
+ uint64_t PendingConnectTimeout;
+ uint64_t InactivityTimeout;
+ uint64_t LastActivity;
+ uint64_t NextHeartbeat;
+ bool bPaused;
+};
+
+
+
+/*************************
+class LoopbreakDescriptor
+*************************/
+
+class LoopbreakDescriptor: public EventableDescriptor
+{
+ public:
+ LoopbreakDescriptor (SOCKET, EventMachine_t*);
+ virtual ~LoopbreakDescriptor() {}
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat() {}
+
+ virtual bool SelectForRead() {return true;}
+ virtual bool SelectForWrite() {return false;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
+};
+
+
+/**************************
+class ConnectionDescriptor
+**************************/
+
+class ConnectionDescriptor: public EventableDescriptor
+{
+ public:
+ ConnectionDescriptor (SOCKET, EventMachine_t*);
+ virtual ~ConnectionDescriptor();
+
+ int SendOutboundData (const char*, unsigned long);
+
+ void SetConnectPending (bool f);
+ virtual void ScheduleClose (bool after_writing);
+ virtual void HandleError();
+
+ void SetNotifyReadable (bool);
+ void SetNotifyWritable (bool);
+ void SetAttached (bool);
+ void SetWatchOnly (bool);
+
+ bool Pause();
+ bool Resume();
+
+ bool IsNotifyReadable(){ return bNotifyReadable; }
+ bool IsNotifyWritable(){ return bNotifyWritable; }
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat();
+
+ virtual bool SelectForRead();
+ virtual bool SelectForWrite();
+
+ // Do we have any data to write? This is used by ShouldDelete.
+ virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+ virtual void StartTls();
+ virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int);
+
+ #ifdef WITH_SSL
+ virtual X509 *GetPeerCert();
+ virtual int GetCipherBits();
+ virtual const char *GetCipherName();
+ virtual const char *GetCipherProtocol();
+ virtual const char *GetSNIHostname();
+ virtual bool VerifySslPeer(const char*);
+ virtual void AcceptSslPeer();
+ #endif
+
+ void SetServerMode() {bIsServer = true;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
+
+ virtual uint64_t GetCommInactivityTimeout();
+ virtual int SetCommInactivityTimeout (uint64_t value);
+
+ virtual int ReportErrorStatus();
+ virtual bool IsConnectPending(){ return bConnectPending; }
+
+ protected:
+ struct OutboundPage {
+ OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
+ void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
+ const char *Buffer;
+ int Length;
+ int Offset;
+ };
+
+ protected:
+ bool bConnectPending;
+
+ bool bNotifyReadable;
+ bool bNotifyWritable;
+
+ bool bReadAttemptedAfterClose;
+ bool bWriteAttemptedAfterClose;
+
+ std::deque<OutboundPage> OutboundPages;
+ int OutboundDataSize;
+
+ #ifdef WITH_SSL
+ SslBox_t *SslBox;
+ std::string CertChainFilename;
+ std::string PrivateKeyFilename;
+ std::string CipherList;
+ std::string EcdhCurve;
+ std::string DhParam;
+ int Protocols;
+ bool bHandshakeSignaled;
+ bool bSslVerifyPeer;
+ bool bSslFailIfNoPeerCert;
+ std::string SniHostName;
+ bool bSslPeerAccepted;
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ bool bGotExtraKqueueEvent;
+ #endif
+
+ bool bIsServer;
+
+ private:
+ void _UpdateEvents();
+ void _UpdateEvents(bool, bool);
+ void _WriteOutboundData();
+ void _DispatchInboundData (const char *buffer, unsigned long size);
+ void _DispatchCiphertext();
+ int _SendRawOutboundData (const char *buffer, unsigned long size);
+ void _CheckHandshakeStatus();
+
+};
+
+
+/************************
+class DatagramDescriptor
+************************/
+
+class DatagramDescriptor: public EventableDescriptor
+{
+ public:
+ DatagramDescriptor (SOCKET, EventMachine_t*);
+ virtual ~DatagramDescriptor();
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat();
+
+ virtual bool SelectForRead() {return true;}
+ virtual bool SelectForWrite();
+
+ int SendOutboundData (const char*, unsigned long);
+ int SendOutboundDatagram (const char*, unsigned long, const char*, int);
+
+ // Do we have any data to write? This is used by ShouldDelete.
+ virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len);
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
+
+ virtual uint64_t GetCommInactivityTimeout();
+ virtual int SetCommInactivityTimeout (uint64_t value);
+
+ protected:
+ struct OutboundPage {
+ OutboundPage (const char *b, int l, struct sockaddr_in6 f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {}
+ void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
+ const char *Buffer;
+ int Length;
+ int Offset;
+ struct sockaddr_in6 From;
+ };
+
+ std::deque<OutboundPage> OutboundPages;
+ int OutboundDataSize;
+
+ struct sockaddr_in6 ReturnAddress;
+};
+
+
+/************************
+class AcceptorDescriptor
+************************/
+
+class AcceptorDescriptor: public EventableDescriptor
+{
+ public:
+ AcceptorDescriptor (SOCKET, EventMachine_t*);
+ virtual ~AcceptorDescriptor();
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat();
+
+ virtual bool SelectForRead() {return true;}
+ virtual bool SelectForWrite() {return false;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); };
+
+ static void StopAcceptor (const uintptr_t binding);
+};
+
+/********************
+class PipeDescriptor
+********************/
+
+#ifdef OS_UNIX
+class PipeDescriptor: public EventableDescriptor
+{
+ public:
+ PipeDescriptor (SOCKET, pid_t, EventMachine_t*);
+ virtual ~PipeDescriptor() NO_EXCEPT_FALSE;
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat();
+
+ virtual bool SelectForRead();
+ virtual bool SelectForWrite();
+
+ int SendOutboundData (const char*, unsigned long);
+ virtual int GetOutboundDataSize() {return OutboundDataSize;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
+
+ virtual bool GetSubprocessPid (pid_t*);
+
+ protected:
+ struct OutboundPage {
+ OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {}
+ void Free() {if (Buffer) free (const_cast<char*>(Buffer)); }
+ const char *Buffer;
+ int Length;
+ int Offset;
+ };
+
+ protected:
+ bool bReadAttemptedAfterClose;
+
+ std::deque<OutboundPage> OutboundPages;
+ int OutboundDataSize;
+
+ pid_t SubprocessPid;
+
+ private:
+ void _DispatchInboundData (const char *buffer, int size);
+};
+#endif // OS_UNIX
+
+
+/************************
+class KeyboardDescriptor
+************************/
+
+class KeyboardDescriptor: public EventableDescriptor
+{
+ public:
+ KeyboardDescriptor (EventMachine_t*);
+ virtual ~KeyboardDescriptor();
+
+ virtual void Read();
+ virtual void Write();
+ virtual void Heartbeat();
+
+ virtual bool SelectForRead() {return true;}
+ virtual bool SelectForWrite() {return false;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }
+
+ protected:
+ bool bReadAttemptedAfterClose;
+
+ private:
+ void _DispatchInboundData (const char *buffer, int size);
+};
+
+
+/***********************
+class InotifyDescriptor
+************************/
+
+class InotifyDescriptor: public EventableDescriptor
+{
+ public:
+ InotifyDescriptor (EventMachine_t*);
+ virtual ~InotifyDescriptor();
+
+ void Read();
+ void Write();
+
+ virtual void Heartbeat() {}
+ virtual bool SelectForRead() {return true;}
+ virtual bool SelectForWrite() {return false;}
+
+ virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return false; }
+ virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return false; }
+};
+
+#endif // __EventableDescriptor__H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.cpp
new file mode 100644
index 0000000..2907485
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.cpp
@@ -0,0 +1,2396 @@
+/*****************************************************************************
+
+$Id$
+
+File: em.cpp
+Date: 06Apr06
+
+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 ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY.
+//#ifdef OS_UNIX
+
+#include "project.h"
+
+/* The numer of max outstanding timers was once a const enum defined in em.h.
+ * Now we define it here so that users can change its value if necessary.
+ */
+static unsigned int MaxOutstandingTimers = 100000;
+
+/* The number of accept() done at once in a single tick when the acceptor
+ * socket becomes readable.
+ */
+static unsigned int SimultaneousAcceptCount = 10;
+
+/* Internal helper to create a socket with SOCK_CLOEXEC set, and fall
+ * back to fcntl'ing it if the headers/runtime don't support it.
+ */
+SOCKET EmSocket (int domain, int type, int protocol)
+{
+ SOCKET sd;
+#ifdef HAVE_SOCKET_CLOEXEC
+ sd = socket (domain, type | SOCK_CLOEXEC, protocol);
+ if (sd == INVALID_SOCKET) {
+ sd = socket (domain, type, protocol);
+ if (sd < 0) {
+ return sd;
+ }
+ SetFdCloexec(sd);
+ }
+#else
+ sd = socket (domain, type, protocol);
+ if (sd == INVALID_SOCKET) {
+ return sd;
+ }
+ SetFdCloexec(sd);
+#endif
+ return sd;
+}
+
+
+/***************************************
+STATIC EventMachine_t::GetMaxTimerCount
+***************************************/
+
+int EventMachine_t::GetMaxTimerCount()
+{
+ return MaxOutstandingTimers;
+}
+
+
+/***************************************
+STATIC EventMachine_t::SetMaxTimerCount
+***************************************/
+
+void EventMachine_t::SetMaxTimerCount (int count)
+{
+ /* Allow a user to increase the maximum number of outstanding timers.
+ * If this gets "too high" (a metric that is of course platform dependent),
+ * bad things will happen like performance problems and possible overuse
+ * of memory.
+ * The actual timer mechanism is very efficient so it's hard to know what
+ * the practical max, but 100,000 shouldn't be too problematical.
+ */
+ if (count < 100)
+ count = 100;
+ MaxOutstandingTimers = count;
+}
+
+int EventMachine_t::GetSimultaneousAcceptCount()
+{
+ return SimultaneousAcceptCount;
+}
+
+void EventMachine_t::SetSimultaneousAcceptCount (int count)
+{
+ if (count < 1)
+ count = 1;
+ SimultaneousAcceptCount = count;
+}
+
+
+/******************************
+EventMachine_t::EventMachine_t
+******************************/
+
+EventMachine_t::EventMachine_t (EMCallback event_callback, Poller_t poller):
+ NumCloseScheduled (0),
+ HeartbeatInterval(2000000),
+ EventCallback (event_callback),
+ LoopBreakerReader (INVALID_SOCKET),
+ LoopBreakerWriter (INVALID_SOCKET),
+ bTerminateSignalReceived (false),
+ Poller (poller),
+ epfd (-1),
+ kqfd (-1)
+ #ifdef HAVE_INOTIFY
+ , inotify (NULL)
+ #endif
+{
+ // Default time-slice is just smaller than one hundred mills.
+ Quantum.tv_sec = 0;
+ Quantum.tv_usec = 90000;
+
+ // Override the requested poller back to default if needed.
+ #if !defined(HAVE_EPOLL) && !defined(HAVE_KQUEUE)
+ Poller = Poller_Default;
+ #endif
+
+ /* Initialize monotonic timekeeping on OS X before the first call to GetRealTime */
+ #ifdef OS_DARWIN
+ (void) mach_timebase_info(&mach_timebase);
+ #endif
+
+ #ifdef OS_WIN32
+ TickCountTickover = 0;
+ LastTickCount = 0;
+ #endif
+
+ // Make sure the current loop time is sane, in case we do any initializations of
+ // objects before we start running.
+ _UpdateTime();
+
+ /* We initialize the network library here (only on Windows of course)
+ * and initialize "loop breakers." Our destructor also does some network-level
+ * cleanup. There's thus an implicit assumption that any given instance of EventMachine_t
+ * will only call ::Run once. Is that a good assumption? Should we move some of these
+ * inits and de-inits into ::Run?
+ */
+ #ifdef OS_WIN32
+ WSADATA w;
+ WSAStartup (MAKEWORD (1, 1), &w);
+ #endif
+
+ _InitializeLoopBreaker();
+ SelectData = new SelectData_t();
+}
+
+
+/*******************************
+EventMachine_t::~EventMachine_t
+*******************************/
+
+EventMachine_t::~EventMachine_t()
+{
+ // Run down descriptors
+ size_t i;
+ for (i = 0; i < NewDescriptors.size(); i++)
+ delete NewDescriptors[i];
+ for (i = 0; i < Descriptors.size(); i++)
+ delete Descriptors[i];
+
+ close (LoopBreakerReader);
+ close (LoopBreakerWriter);
+
+ // Remove any file watch descriptors
+ while(!Files.empty()) {
+ std::map<int, Bindable_t*>::iterator f = Files.begin();
+ UnwatchFile (f->first);
+ }
+
+ if (epfd != -1)
+ close (epfd);
+ if (kqfd != -1)
+ close (kqfd);
+
+ delete SelectData;
+}
+
+
+/****************************
+EventMachine_t::ScheduleHalt
+****************************/
+
+void EventMachine_t::ScheduleHalt()
+{
+ /* This is how we stop the machine.
+ * This can be called by clients. Signal handlers will probably
+ * set the global flag.
+ * For now this means there can only be one EventMachine ever running at a time.
+ *
+ * IMPORTANT: keep this light, fast, and async-safe. Don't do anything frisky in here,
+ * because it may be called from signal handlers invoked from code that we don't
+ * control. At this writing (20Sep06), EM does NOT install any signal handlers of
+ * its own.
+ *
+ * We need a FAQ. And one of the questions is: how do I stop EM when Ctrl-C happens?
+ * The answer is to call evma_stop_machine, which calls here, from a SIGINT handler.
+ */
+ bTerminateSignalReceived = true;
+
+ /* Signal the loopbreaker so we break out of long-running select/epoll/kqueue and
+ * notice the halt boolean is set. Signalling the loopbreaker also uses a single
+ * signal-safe syscall.
+ */
+ SignalLoopBreaker();
+}
+
+bool EventMachine_t::Stopping()
+{
+ return bTerminateSignalReceived;
+}
+
+/*******************************
+EventMachine_t::SetTimerQuantum
+*******************************/
+
+void EventMachine_t::SetTimerQuantum (int interval)
+{
+ /* We get a timer-quantum expressed in milliseconds.
+ */
+
+ if ((interval < 5) || (interval > 5*60*1000))
+ throw std::runtime_error ("invalid timer-quantum");
+
+ Quantum.tv_sec = interval / 1000;
+ Quantum.tv_usec = (interval % 1000) * 1000;
+}
+
+
+/*************************************
+(STATIC) EventMachine_t::SetuidString
+*************************************/
+
+#ifdef OS_UNIX
+void EventMachine_t::SetuidString (const char *username)
+{
+ /* This method takes a caller-supplied username and tries to setuid
+ * to that user. There is no meaningful implementation (and no error)
+ * on Windows. On Unix, a failure to setuid the caller-supplied string
+ * causes a fatal abort, because presumably the program is calling here
+ * in order to fulfill a security requirement. If we fail silently,
+ * the user may continue to run with too much privilege.
+ *
+ * TODO, we need to decide on and document a way of generating C++ level errors
+ * that can be wrapped in documented Ruby exceptions, so users can catch
+ * and handle them. And distinguish it from errors that we WON'T let the Ruby
+ * user catch (like security-violations and resource-overallocation).
+ * A setuid failure here would be in the latter category.
+ */
+
+ if (!username || !*username)
+ throw std::runtime_error ("setuid_string failed: no username specified");
+
+ errno = 0;
+ struct passwd *p = getpwnam (username);
+ if (!p) {
+ if (errno) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "setuid_string failed: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ } else {
+ throw std::runtime_error ("setuid_string failed: unknown username");
+ }
+ }
+
+ if (setuid (p->pw_uid) != 0)
+ throw std::runtime_error ("setuid_string failed: no setuid");
+
+ // Success.
+}
+#else
+void EventMachine_t::SetuidString (const char *username UNUSED) { }
+#endif
+
+/****************************************
+(STATIC) EventMachine_t::SetRlimitNofile
+****************************************/
+
+#ifdef OS_UNIX
+int EventMachine_t::SetRlimitNofile (int nofiles)
+{
+ struct rlimit rlim;
+ getrlimit (RLIMIT_NOFILE, &rlim);
+ if (nofiles >= 0) {
+ rlim.rlim_cur = nofiles;
+ if ((unsigned int)nofiles > rlim.rlim_max)
+ rlim.rlim_max = nofiles;
+ setrlimit (RLIMIT_NOFILE, &rlim);
+ // ignore the error return, for now at least.
+ // TODO, emit an error message someday when we have proper debug levels.
+ }
+ getrlimit (RLIMIT_NOFILE, &rlim);
+ return rlim.rlim_cur;
+}
+#else
+int EventMachine_t::SetRlimitNofile (int nofiles UNUSED) { return 0; }
+#endif
+
+/*********************************
+EventMachine_t::SignalLoopBreaker
+*********************************/
+
+void EventMachine_t::SignalLoopBreaker()
+{
+ #ifdef OS_UNIX
+ (void)write (LoopBreakerWriter, "", 1);
+ #endif
+ #ifdef OS_WIN32
+ sendto (LoopBreakerReader, "", 0, 0, (struct sockaddr*)&(LoopBreakerTarget), sizeof(LoopBreakerTarget));
+ #endif
+}
+
+
+/**************************************
+EventMachine_t::_InitializeLoopBreaker
+**************************************/
+
+void EventMachine_t::_InitializeLoopBreaker()
+{
+ /* A "loop-breaker" is a socket-descriptor that we can write to in order
+ * to break the main select loop. Primarily useful for things running on
+ * threads other than the main EM thread, so they can trigger processing
+ * of events that arise exogenously to the EM.
+ * Keep the loop-breaker pipe out of the main descriptor set, otherwise
+ * its events will get passed on to user code.
+ */
+
+ #ifdef OS_UNIX
+ int fd[2];
+#if defined (HAVE_CLOEXEC) && defined (HAVE_PIPE2)
+ int pipestatus = pipe2(fd, O_CLOEXEC);
+ if (pipestatus < 0) {
+ if (pipe(fd))
+ throw std::runtime_error (strerror(errno));
+ }
+#else
+ if (pipe (fd))
+ throw std::runtime_error (strerror(errno));
+#endif
+ if (!SetFdCloexec(fd[0]) || !SetFdCloexec(fd[1]))
+ throw std::runtime_error (strerror(errno));
+
+ LoopBreakerWriter = fd[1];
+ LoopBreakerReader = fd[0];
+
+ /* 16Jan11: Make sure the pipe is non-blocking, so more than 65k loopbreaks
+ * in one tick do not fill up the pipe and block the process on write() */
+ SetSocketNonblocking (LoopBreakerWriter);
+ #endif
+
+ #ifdef OS_WIN32
+ SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0);
+ if (sd == INVALID_SOCKET)
+ throw std::runtime_error ("no loop breaker socket");
+ SetSocketNonblocking (sd);
+
+ memset (&LoopBreakerTarget, 0, sizeof(LoopBreakerTarget));
+ LoopBreakerTarget.sin_family = AF_INET;
+ LoopBreakerTarget.sin_addr.s_addr = inet_addr ("127.0.0.1");
+
+ srand ((int)time(NULL));
+ int i;
+ for (i=0; i < 100; i++) {
+ int r = (rand() % 10000) + 20000;
+ LoopBreakerTarget.sin_port = htons (r);
+ if (bind (sd, (struct sockaddr*)&LoopBreakerTarget, sizeof(LoopBreakerTarget)) == 0)
+ break;
+ }
+
+ if (i == 100)
+ throw std::runtime_error ("no loop breaker");
+ LoopBreakerReader = sd;
+ #endif
+
+ #ifdef HAVE_EPOLL
+ if (Poller == Poller_Epoll) {
+ epfd = epoll_create (MaxEpollDescriptors);
+ if (epfd == -1) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ int cloexec = fcntl (epfd, F_GETFD, 0);
+ assert (cloexec >= 0);
+ cloexec |= FD_CLOEXEC;
+ fcntl (epfd, F_SETFD, cloexec);
+
+ assert (LoopBreakerReader >= 0);
+ LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+ assert (ld);
+ Add (ld);
+ }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (Poller == Poller_Kqueue) {
+ kqfd = kqueue();
+ if (kqfd == -1) {
+ char buf[200];
+ snprintf (buf, sizeof(buf)-1, "unable to create kqueue descriptor: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ // cloexec not needed. By definition, kqueues are not carried across forks.
+
+ assert (LoopBreakerReader >= 0);
+ LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this);
+ assert (ld);
+ Add (ld);
+ }
+ #endif
+}
+
+/***************************
+EventMachine_t::_UpdateTime
+***************************/
+
+void EventMachine_t::_UpdateTime()
+{
+ MyCurrentLoopTime = GetRealTime();
+}
+
+/***************************
+EventMachine_t::GetRealTime
+***************************/
+
+// Two great writeups of cross-platform monotonic time are at:
+// http://www.python.org/dev/peps/pep-0418
+// http://nadeausoftware.com/articles/2012/04/c_c_tip_how_measure_elapsed_real_time_benchmarking
+// Uncomment the #pragma messages to confirm which compile-time option was used
+uint64_t EventMachine_t::GetRealTime()
+{
+ uint64_t current_time;
+
+ #if defined(HAVE_CONST_CLOCK_MONOTONIC_RAW)
+ // #pragma message "GetRealTime: clock_gettime CLOCK_MONOTONIC_RAW"
+ // Linux 2.6.28 and above
+ struct timespec tv;
+ clock_gettime (CLOCK_MONOTONIC_RAW, &tv);
+ current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)((tv.tv_nsec)/1000));
+
+ #elif defined(HAVE_CONST_CLOCK_MONOTONIC)
+ // #pragma message "GetRealTime: clock_gettime CLOCK_MONOTONIC"
+ // Linux, FreeBSD 5.0 and above, Solaris 8 and above, OpenBSD, NetBSD, DragonflyBSD
+ struct timespec tv;
+ clock_gettime (CLOCK_MONOTONIC, &tv);
+ current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)((tv.tv_nsec)/1000));
+
+ #elif defined(HAVE_GETHRTIME)
+ // #pragma message "GetRealTime: gethrtime"
+ // Solaris and HP-UX
+ current_time = (uint64_t)gethrtime() / 1000;
+
+ #elif defined(OS_DARWIN)
+ // #pragma message "GetRealTime: mach_absolute_time"
+ // Mac OS X
+ // https://developer.apple.com/library/mac/qa/qa1398/_index.html
+ current_time = mach_absolute_time() * mach_timebase.numer / mach_timebase.denom / 1000;
+
+ #elif defined(OS_UNIX)
+ // #pragma message "GetRealTime: gettimeofday"
+ // Unix fallback
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+ current_time = (((uint64_t)(tv.tv_sec)) * 1000000LL) + ((uint64_t)(tv.tv_usec));
+
+ #elif defined(OS_WIN32)
+ // #pragma message "GetRealTime: GetTickCount"
+ // Future improvement: use GetTickCount64 in Windows Vista / Server 2008
+ unsigned tick = GetTickCount();
+ if (tick < LastTickCount)
+ TickCountTickover += 1;
+ LastTickCount = tick;
+ current_time = ((uint64_t)TickCountTickover << 32) + (uint64_t)tick;
+ current_time *= 1000; // convert to microseconds
+
+ #else
+ // #pragma message "GetRealTime: time"
+ // Universal fallback
+ current_time = (uint64_t)time(NULL) * 1000000LL;
+ #endif
+
+ return current_time;
+}
+
+/***********************************
+EventMachine_t::_DispatchHeartbeats
+***********************************/
+
+void EventMachine_t::_DispatchHeartbeats()
+{
+ // Store the first processed heartbeat descriptor and bail out if
+ // we see it again. This fixes an infinite loop in case the system time
+ // is changed out from underneath MyCurrentLoopTime.
+ const EventableDescriptor *head = NULL;
+
+ while (true) {
+ std::multimap<uint64_t,EventableDescriptor*>::iterator i = Heartbeats.begin();
+ if (i == Heartbeats.end())
+ break;
+ if (i->first > MyCurrentLoopTime)
+ break;
+
+ EventableDescriptor *ed = i->second;
+ if (ed == head)
+ break;
+
+ ed->Heartbeat();
+ QueueHeartbeat(ed);
+
+ if (head == NULL)
+ head = ed;
+ }
+}
+
+/******************************
+EventMachine_t::QueueHeartbeat
+******************************/
+
+void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed)
+{
+ uint64_t heartbeat = ed->GetNextHeartbeat();
+
+ if (heartbeat) {
+ #ifndef HAVE_MAKE_PAIR
+ Heartbeats.insert (std::multimap<uint64_t,EventableDescriptor*>::value_type (heartbeat, ed));
+ #else
+ Heartbeats.insert (std::make_pair (heartbeat, ed));
+ #endif
+ }
+}
+
+/******************************
+EventMachine_t::ClearHeartbeat
+******************************/
+
+void EventMachine_t::ClearHeartbeat(uint64_t key, EventableDescriptor* ed)
+{
+ std::multimap<uint64_t,EventableDescriptor*>::iterator it;
+ std::pair<std::multimap<uint64_t,EventableDescriptor*>::iterator,std::multimap<uint64_t,EventableDescriptor*>::iterator> ret;
+ ret = Heartbeats.equal_range (key);
+ for (it = ret.first; it != ret.second; ++it) {
+ if (it->second == ed) {
+ Heartbeats.erase (it);
+ break;
+ }
+ }
+}
+
+/*******************
+EventMachine_t::Run
+*******************/
+
+void EventMachine_t::Run()
+{
+ while (RunOnce()) ;
+}
+
+/***********************
+EventMachine_t::RunOnce
+***********************/
+
+bool EventMachine_t::RunOnce()
+{
+ _UpdateTime();
+ _RunTimers();
+
+ /* _Add must precede _Modify because the same descriptor might
+ * be on both lists during the same pass through the machine,
+ * and to modify a descriptor before adding it would fail.
+ */
+ _AddNewDescriptors();
+ _ModifyDescriptors();
+
+ switch (Poller) {
+ case Poller_Epoll:
+ _RunEpollOnce();
+ break;
+ case Poller_Kqueue:
+ _RunKqueueOnce();
+ break;
+ case Poller_Default:
+ _RunSelectOnce();
+ break;
+ }
+
+ _DispatchHeartbeats();
+ _CleanupSockets();
+
+ if (bTerminateSignalReceived)
+ return false;
+
+ return true;
+}
+
+
+/*****************************
+EventMachine_t::_RunEpollOnce
+*****************************/
+
+void EventMachine_t::_RunEpollOnce()
+{
+ #ifdef HAVE_EPOLL
+ assert (epfd != -1);
+ int s;
+
+ timeval tv = _TimeTilNextEvent();
+
+ #ifdef BUILD_FOR_RUBY
+ int ret = 0;
+
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
+ if ((ret = rb_wait_for_single_fd(epfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) {
+ #else
+ fd_set fdreads;
+
+ FD_ZERO(&fdreads);
+ FD_SET(epfd, &fdreads);
+
+ if ((ret = rb_thread_select(epfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
+ #endif
+ if (ret == -1) {
+ assert(errno != EINVAL);
+ assert(errno != EBADF);
+ }
+ return;
+ }
+
+ TRAP_BEG;
+ s = epoll_wait (epfd, epoll_events, MaxEvents, 0);
+ TRAP_END;
+ #else
+ int duration = 0;
+ duration = duration + (tv.tv_sec * 1000);
+ duration = duration + (tv.tv_usec / 1000);
+ s = epoll_wait (epfd, epoll_events, MaxEvents, duration);
+ #endif
+
+ if (s > 0) {
+ for (int i=0; i < s; i++) {
+ EventableDescriptor *ed = (EventableDescriptor*) epoll_events[i].data.ptr;
+
+ if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
+ continue;
+
+ assert(ed->GetSocket() != INVALID_SOCKET);
+
+ if (epoll_events[i].events & EPOLLIN)
+ ed->Read();
+ if (epoll_events[i].events & EPOLLOUT)
+ ed->Write();
+ if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
+ ed->HandleError();
+ }
+ }
+ else if (s < 0) {
+ // epoll_wait can fail on error in a handful of ways.
+ // If this happens, then wait for a little while to avoid busy-looping.
+ // If the error was EINTR, we probably caught SIGCHLD or something,
+ // so keep the wait short.
+ timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
+ EmSelect (0, NULL, NULL, NULL, &tv);
+ }
+ #else
+ throw std::runtime_error ("epoll is not implemented on this platform");
+ #endif
+}
+
+
+/******************************
+EventMachine_t::_RunKqueueOnce
+******************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_RunKqueueOnce()
+{
+ assert (kqfd != -1);
+ int k;
+
+ timeval tv = _TimeTilNextEvent();
+
+ struct timespec ts;
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ #ifdef BUILD_FOR_RUBY
+ int ret = 0;
+
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
+ if ((ret = rb_wait_for_single_fd(kqfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) {
+ #else
+ fd_set fdreads;
+
+ FD_ZERO(&fdreads);
+ FD_SET(kqfd, &fdreads);
+
+ if ((ret = rb_thread_select(kqfd + 1, &fdreads, NULL, NULL, &tv)) < 1) {
+ #endif
+ if (ret == -1) {
+ assert(errno != EINVAL);
+ assert(errno != EBADF);
+ }
+ return;
+ }
+
+ TRAP_BEG;
+ ts.tv_sec = ts.tv_nsec = 0;
+ k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
+ TRAP_END;
+ #else
+ k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts);
+ #endif
+
+ struct kevent *ke = Karray;
+ while (k > 0) {
+ switch (ke->filter)
+ {
+ case EVFILT_VNODE:
+ _HandleKqueueFileEvent (ke);
+ break;
+
+ case EVFILT_PROC:
+ _HandleKqueuePidEvent (ke);
+ break;
+
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ EventableDescriptor *ed = (EventableDescriptor*) (ke->udata);
+ assert (ed);
+
+ if (ed->IsWatchOnly() && ed->GetSocket() == INVALID_SOCKET)
+ break;
+
+ if (ke->filter == EVFILT_READ)
+ ed->Read();
+ else if (ke->filter == EVFILT_WRITE)
+ ed->Write();
+ else
+ std::cerr << "Discarding unknown kqueue event " << ke->filter << std::endl;
+
+ break;
+ }
+
+ --k;
+ ++ke;
+ }
+
+ // TODO, replace this with rb_thread_blocking_region for 1.9 builds.
+ #ifdef BUILD_FOR_RUBY
+ if (!rb_thread_alone()) {
+ rb_thread_schedule();
+ }
+ #endif
+}
+#else
+void EventMachine_t::_RunKqueueOnce()
+{
+ throw std::runtime_error ("kqueue is not implemented on this platform");
+}
+#endif
+
+
+/*********************************
+EventMachine_t::_TimeTilNextEvent
+*********************************/
+
+timeval EventMachine_t::_TimeTilNextEvent()
+{
+ // 29jul11: Changed calculation base from MyCurrentLoopTime to the
+ // real time. As MyCurrentLoopTime is set at the beginning of an
+ // iteration and this calculation is done at the end, evenmachine
+ // will potentially oversleep by the amount of time the iteration
+ // took to execute.
+ uint64_t next_event = 0;
+ uint64_t current_time = GetRealTime();
+
+ if (!Heartbeats.empty()) {
+ std::multimap<uint64_t,EventableDescriptor*>::iterator heartbeats = Heartbeats.begin();
+ next_event = heartbeats->first;
+ }
+
+ if (!Timers.empty()) {
+ std::multimap<uint64_t,Timer_t>::iterator timers = Timers.begin();
+ if (next_event == 0 || timers->first < next_event)
+ next_event = timers->first;
+ }
+
+ if (!NewDescriptors.empty() || !ModifiedDescriptors.empty()) {
+ next_event = current_time;
+ }
+
+ timeval tv;
+
+ if (NumCloseScheduled > 0 || bTerminateSignalReceived) {
+ tv.tv_sec = tv.tv_usec = 0;
+ } else if (next_event == 0) {
+ tv = Quantum;
+ } else {
+ if (next_event > current_time) {
+ uint64_t duration = next_event - current_time;
+ tv.tv_sec = duration / 1000000;
+ tv.tv_usec = duration % 1000000;
+ } else {
+ tv.tv_sec = tv.tv_usec = 0;
+ }
+ }
+
+ return tv;
+}
+
+/*******************************
+EventMachine_t::_CleanupSockets
+*******************************/
+
+void EventMachine_t::_CleanupSockets()
+{
+ // TODO, rip this out and only delete the descriptors we know have died,
+ // rather than traversing the whole list.
+ // Modified 05Jan08 per suggestions by Chris Heath. It's possible that
+ // an EventableDescriptor will have a descriptor value of -1. That will
+ // happen if EventableDescriptor::Close was called on it. In that case,
+ // don't call epoll_ctl to remove the socket's filters from the epoll set.
+ // According to the epoll docs, this happens automatically when the
+ // descriptor is closed anyway. This is different from the case where
+ // the socket has already been closed but the descriptor in the ED object
+ // hasn't yet been set to INVALID_SOCKET.
+ // In kqueue, closing a descriptor automatically removes its event filters.
+ int i, j;
+ int nSockets = Descriptors.size();
+ for (i=0, j=0; i < nSockets; i++) {
+ EventableDescriptor *ed = Descriptors[i];
+ assert (ed);
+ if (ed->ShouldDelete()) {
+ #ifdef HAVE_EPOLL
+ if (Poller == Poller_Epoll) {
+ assert (epfd != -1);
+ if (ed->GetSocket() != INVALID_SOCKET) {
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
+ if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+ ModifiedDescriptors.erase(ed);
+ }
+ #endif
+ delete ed;
+ }
+ else
+ Descriptors [j++] = ed;
+ }
+ while ((size_t)j < Descriptors.size())
+ Descriptors.pop_back();
+}
+
+/*********************************
+EventMachine_t::_ModifyEpollEvent
+*********************************/
+
+#ifdef HAVE_EPOLL
+void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed)
+{
+ if (Poller == Poller_Epoll) {
+ assert (epfd != -1);
+ assert (ed);
+ assert (ed->GetSocket() != INVALID_SOCKET);
+ int e = epoll_ctl (epfd, EPOLL_CTL_MOD, ed->GetSocket(), ed->GetEpollEvent());
+ if (e) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to modify epoll event: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+}
+#else
+void EventMachine_t::_ModifyEpollEvent (EventableDescriptor *ed UNUSED) { }
+#endif
+
+
+/**************************
+SelectData_t::SelectData_t
+**************************/
+
+SelectData_t::SelectData_t()
+{
+ maxsocket = 0;
+ rb_fd_init (&fdreads);
+ rb_fd_init (&fdwrites);
+ rb_fd_init (&fderrors);
+}
+
+SelectData_t::~SelectData_t()
+{
+ rb_fd_term (&fdreads);
+ rb_fd_term (&fdwrites);
+ rb_fd_term (&fderrors);
+}
+
+#ifdef BUILD_FOR_RUBY
+/*****************
+_SelectDataSelect
+*****************/
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+static VALUE _SelectDataSelect (void *v)
+{
+ SelectData_t *sd = (SelectData_t*)v;
+ sd->nSockets = rb_fd_select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), &(sd->fderrors), &(sd->tv));
+ return Qnil;
+}
+#endif
+
+/*********************
+SelectData_t::_Select
+*********************/
+
+int SelectData_t::_Select()
+{
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+ // added in ruby 1.9.3
+ rb_thread_call_without_gvl ((void *(*)(void *))_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0);
+ return nSockets;
+ #elif defined(HAVE_TBR)
+ // added in ruby 1.9.1, deprecated in ruby 2.0.0
+ rb_thread_blocking_region (_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0);
+ return nSockets;
+ #else
+ return EmSelect (maxsocket+1, &fdreads, &fdwrites, &fderrors, &tv);
+ #endif
+}
+#endif
+
+void SelectData_t::_Clear()
+{
+ maxsocket = 0;
+ rb_fd_zero (&fdreads);
+ rb_fd_zero (&fdwrites);
+ rb_fd_zero (&fderrors);
+}
+
+/******************************
+EventMachine_t::_RunSelectOnce
+******************************/
+
+void EventMachine_t::_RunSelectOnce()
+{
+ // Crank the event machine once.
+ // If there are no descriptors to process, then sleep
+ // for a few hundred mills to avoid busy-looping.
+ // This is based on a select loop. Alternately provide epoll
+ // if we know we're running on a 2.6 kernel.
+ // epoll will be effective if we provide it as an alternative,
+ // however it has the same problem interoperating with Ruby
+ // threads that select does.
+
+ // Get ready for select()
+ SelectData->_Clear();
+
+ // Always read the loop-breaker reader.
+ // Changed 23Aug06, provisionally implemented for Windows with a UDP socket
+ // running on localhost with a randomly-chosen port. (*Puke*)
+ // Windows has a version of the Unix pipe() library function, but it doesn't
+ // give you back descriptors that are selectable.
+ rb_fd_set (LoopBreakerReader, &(SelectData->fdreads));
+ if (SelectData->maxsocket < LoopBreakerReader)
+ SelectData->maxsocket = LoopBreakerReader;
+
+ // prepare the sockets for reading and writing
+ size_t i;
+ for (i = 0; i < Descriptors.size(); i++) {
+ EventableDescriptor *ed = Descriptors[i];
+ assert (ed);
+ SOCKET sd = ed->GetSocket();
+ if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
+ continue;
+ assert (sd != INVALID_SOCKET);
+
+ if (ed->SelectForRead())
+ rb_fd_set (sd, &(SelectData->fdreads));
+ if (ed->SelectForWrite())
+ rb_fd_set (sd, &(SelectData->fdwrites));
+
+ #ifdef OS_WIN32
+ /* 21Sep09: on windows, a non-blocking connect() that fails does not come up as writable.
+ Instead, it is added to the error set. See http://www.mail-archive.com/openssl-users@openssl.org/msg58500.html
+ */
+ if (ed->IsConnectPending())
+ rb_fd_set (sd, &(SelectData->fderrors));
+ #endif
+
+ if (SelectData->maxsocket < sd)
+ SelectData->maxsocket = sd;
+ }
+
+
+ { // read and write the sockets
+ //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000.
+ //timeval tv = Quantum;
+ SelectData->tv = _TimeTilNextEvent();
+ int s = SelectData->_Select();
+ //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0);
+ //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv));
+ //int s = SelectData.nSockets;
+ if (s > 0) {
+ /* Changed 01Jun07. We used to handle the Loop-breaker right here.
+ * Now we do it AFTER all the regular descriptors. There's an
+ * incredibly important and subtle reason for this. Code on
+ * loop breakers is sometimes used to cause the reactor core to
+ * cycle (for example, to allow outbound network buffers to drain).
+ * If a loop-breaker handler reschedules itself (say, after determining
+ * that the write buffers are still too full), then it will execute
+ * IMMEDIATELY if _ReadLoopBreaker is done here instead of after
+ * the other descriptors are processed. That defeats the whole purpose.
+ */
+ for (i=0; i < Descriptors.size(); i++) {
+ EventableDescriptor *ed = Descriptors[i];
+ assert (ed);
+ SOCKET sd = ed->GetSocket();
+ if (ed->IsWatchOnly() && sd == INVALID_SOCKET)
+ continue;
+ assert (sd != INVALID_SOCKET);
+
+ if (rb_fd_isset (sd, &(SelectData->fdwrites))) {
+ // Double-check SelectForWrite() still returns true. If not, one of the callbacks must have
+ // modified some value since we checked SelectForWrite() earlier in this method.
+ if (ed->SelectForWrite())
+ ed->Write();
+ }
+ if (rb_fd_isset (sd, &(SelectData->fdreads)))
+ ed->Read();
+ if (rb_fd_isset (sd, &(SelectData->fderrors)))
+ ed->HandleError();
+ }
+
+ if (rb_fd_isset (LoopBreakerReader, &(SelectData->fdreads)))
+ _ReadLoopBreaker();
+ }
+ else if (s < 0) {
+ switch (errno) {
+ case EBADF:
+ _CleanBadDescriptors();
+ break;
+ case EINVAL:
+ throw std::runtime_error ("Somehow EM passed an invalid nfds or invalid timeout to select(2), please report this!");
+ break;
+ default:
+ // select can fail on error in a handful of ways.
+ // If this happens, then wait for a little while to avoid busy-looping.
+ // If the error was EINTR, we probably caught SIGCHLD or something,
+ // so keep the wait short.
+ timeval tv = {0, ((errno == EINTR) ? 5 : 50) * 1000};
+ EmSelect (0, NULL, NULL, NULL, &tv);
+ }
+ }
+ }
+}
+
+void EventMachine_t::_CleanBadDescriptors()
+{
+ size_t i;
+
+ for (i = 0; i < Descriptors.size(); i++) {
+ EventableDescriptor *ed = Descriptors[i];
+ if (ed->ShouldDelete())
+ continue;
+
+ SOCKET sd = ed->GetSocket();
+
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ rb_fdset_t fds;
+ rb_fd_init(&fds);
+ rb_fd_set(sd, &fds);
+
+ int ret = rb_fd_select(sd + 1, &fds, NULL, NULL, &tv);
+ rb_fd_term(&fds);
+
+ if (ret == -1) {
+ if (errno == EBADF)
+ ed->ScheduleClose(false);
+ }
+ }
+}
+
+/********************************
+EventMachine_t::_ReadLoopBreaker
+********************************/
+
+void EventMachine_t::_ReadLoopBreaker()
+{
+ /* The loop breaker has selected readable.
+ * Read it ONCE (it may block if we try to read it twice)
+ * and send a loop-break event back to user code.
+ */
+ char buffer [1024];
+ (void)read (LoopBreakerReader, buffer, sizeof(buffer));
+ if (EventCallback)
+ (*EventCallback)(0, EM_LOOPBREAK_SIGNAL, "", 0);
+}
+
+
+/**************************
+EventMachine_t::_RunTimers
+**************************/
+
+void EventMachine_t::_RunTimers()
+{
+ // These are caller-defined timer handlers.
+ // We rely on the fact that multimaps sort by their keys to avoid
+ // inspecting the whole list every time we come here.
+ // Just keep inspecting and processing the list head until we hit
+ // one that hasn't expired yet.
+
+ while (true) {
+ std::multimap<uint64_t,Timer_t>::iterator i = Timers.begin();
+ if (i == Timers.end())
+ break;
+ if (i->first > MyCurrentLoopTime)
+ break;
+ if (EventCallback)
+ (*EventCallback) (0, EM_TIMER_FIRED, NULL, i->second.GetBinding());
+ Timers.erase (i);
+ }
+}
+
+
+
+/***********************************
+EventMachine_t::InstallOneshotTimer
+***********************************/
+
+const uintptr_t EventMachine_t::InstallOneshotTimer (uint64_t milliseconds)
+{
+ if (Timers.size() > MaxOutstandingTimers)
+ return false;
+
+ uint64_t fire_at = GetRealTime();
+ fire_at += ((uint64_t)milliseconds) * 1000LL;
+
+ Timer_t t;
+ #ifndef HAVE_MAKE_PAIR
+ std::multimap<uint64_t,Timer_t>::iterator i = Timers.insert (std::multimap<uint64_t,Timer_t>::value_type (fire_at, t));
+ #else
+ std::multimap<uint64_t,Timer_t>::iterator i = Timers.insert (std::make_pair (fire_at, t));
+ #endif
+ return i->second.GetBinding();
+}
+
+
+/*******************************
+EventMachine_t::ConnectToServer
+*******************************/
+
+const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind_port, const char *server, int port)
+{
+ /* We want to spend no more than a few seconds waiting for a connection
+ * to a remote host. So we use a nonblocking connect.
+ * Linux disobeys the usual rules for nonblocking connects.
+ * Per Stevens (UNP p.410), you expect a nonblocking connect to select
+ * both readable and writable on error, and not to return EINPROGRESS
+ * if the connect can be fulfilled immediately. Linux violates both
+ * of these expectations.
+ * Any kind of nonblocking connect on Linux returns EINPROGRESS.
+ * The socket will then return writable when the disposition of the
+ * connect is known, but it will not also be readable in case of
+ * error! Weirdly, it will be readable in case there is data to read!!!
+ * (Which can happen with protocols like SSH and SMTP.)
+ * I suppose if you were so inclined you could consider this logical,
+ * but it's not the way Unix has historically done it.
+ * So we ignore the readable flag and read getsockopt to see if there
+ * was an error connecting. A select timeout works as expected.
+ * In regard to getsockopt: Linux does the Berkeley-style thing,
+ * not the Solaris-style, and returns zero with the error code in
+ * the error parameter.
+ * Return the binding-text of the newly-created pending connection,
+ * or NULL if there was a problem.
+ */
+
+ if (!server || !*server || !port)
+ throw std::runtime_error ("invalid server or port");
+
+ struct sockaddr_storage bind_as;
+ size_t bind_as_len = sizeof bind_as;
+ int gai = name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_as, &bind_as_len);
+ if (gai != 0) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to resolve address: %s", gai_strerror(gai));
+ throw std::runtime_error (buf);
+ }
+
+ SOCKET sd = EmSocket (bind_as.ss_family, SOCK_STREAM, 0);
+ if (sd == INVALID_SOCKET) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+
+ // From here on, ALL error returns must close the socket.
+ // Set the new socket nonblocking.
+ if (!SetSocketNonblocking (sd)) {
+ close (sd);
+ throw std::runtime_error ("unable to set socket as non-blocking");
+ }
+ // Disable slow-start (Nagle algorithm).
+ int one = 1;
+ setsockopt (sd, IPPROTO_TCP, TCP_NODELAY, (char*) &one, sizeof(one));
+ // Set reuseaddr to improve performance on restarts
+ setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one));
+
+ if (bind_addr) {
+ struct sockaddr_storage bind_to;
+ size_t bind_to_len = sizeof bind_to;
+ gai = name2address (bind_addr, bind_port, SOCK_STREAM, (struct sockaddr *)&bind_to, &bind_to_len);
+ if (gai != 0) {
+ close (sd);
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "invalid bind address: %s", gai_strerror(gai));
+ throw std::runtime_error (buf);
+ }
+ if (bind (sd, (struct sockaddr *)&bind_to, bind_to_len) < 0) {
+ close (sd);
+ throw std::runtime_error ("couldn't bind to address");
+ }
+ }
+
+ uintptr_t out = 0;
+
+ #ifdef OS_UNIX
+ int e_reason = 0;
+ if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
+ // This is a connect success, which Linux appears
+ // never to give when the socket is nonblocking,
+ // even if the connection is intramachine or to
+ // localhost.
+
+ /* Changed this branch 08Aug06. Evidently some kernels
+ * (FreeBSD for example) will actually return success from
+ * a nonblocking connect. This is a pretty simple case,
+ * just set up the new connection and clear the pending flag.
+ * Thanks to Chris Ochs for helping track this down.
+ * This branch never gets taken on Linux or (oddly) OSX.
+ * The original behavior was to throw an unimplemented,
+ * which the user saw as a fatal exception. Very unfriendly.
+ *
+ * Tweaked 10Aug06. Even though the connect disposition is
+ * known, we still set the connect-pending flag. That way
+ * some needed initialization will happen in the ConnectionDescriptor.
+ * (To wit, the ConnectionCompleted event gets sent to the client.)
+ */
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+ cd->SetConnectPending (true);
+ Add (cd);
+ out = cd->GetBinding();
+ }
+ else if (errno == EINPROGRESS) {
+ // Errno will generally always be EINPROGRESS, but on Linux
+ // we have to look at getsockopt to be sure what really happened.
+ int error = 0;
+ socklen_t len;
+ len = sizeof(error);
+ int o = getsockopt (sd, SOL_SOCKET, SO_ERROR, &error, &len);
+ if ((o == 0) && (error == 0)) {
+ // Here, there's no disposition.
+ // Put the connection on the stack and wait for it to complete
+ // or time out.
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+ cd->SetConnectPending (true);
+ Add (cd);
+ out = cd->GetBinding();
+ } else {
+ // Fall through to the !out case below.
+ e_reason = error;
+ }
+ }
+ else {
+ // The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc).
+ // Fall through to the !out case below
+ e_reason = errno;
+ }
+
+ if (!out) {
+ /* This could be connection refused or some such thing.
+ * We will come here on Linux if a localhost connection fails.
+ * Changed 16Jul06: Originally this branch was a no-op, and
+ * we'd drop down to the end of the method, close the socket,
+ * and return NULL, which would cause the caller to GET A
+ * FATAL EXCEPTION. Now we keep the socket around but schedule an
+ * immediate close on it, so the caller will get a close-event
+ * scheduled on it. This was only an issue for localhost connections
+ * to non-listening ports. We may eventually need to revise this
+ * revised behavior, in case it causes problems like making it hard
+ * for people to know that a failure occurred.
+ */
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+ cd->SetUnbindReasonCode (e_reason);
+ cd->ScheduleClose (false);
+ Add (cd);
+ out = cd->GetBinding();
+ }
+ #endif
+
+ #ifdef OS_WIN32
+ if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) {
+ // This is a connect success, which Windows appears
+ // never to give when the socket is nonblocking,
+ // even if the connection is intramachine or to
+ // localhost.
+ throw std::runtime_error ("unimplemented");
+ }
+ else if (WSAGetLastError() == WSAEWOULDBLOCK) {
+ // Here, there's no disposition.
+ // Windows appears not to surface refused connections or
+ // such stuff at this point.
+ // Put the connection on the stack and wait for it to complete
+ // or time out.
+ ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+ cd->SetConnectPending (true);
+ Add (cd);
+ out = cd->GetBinding();
+ }
+ else {
+ // The error from connect was something other then WSAEWOULDBLOCK.
+ }
+
+ #endif
+
+ if (!out)
+ close (sd);
+ return out;
+}
+
+/***********************************
+EventMachine_t::ConnectToUnixServer
+***********************************/
+
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server)
+{
+ /* Connect to a Unix-domain server, which by definition is running
+ * on the same host.
+ * There is no meaningful implementation on Windows.
+ * There's no need to do a nonblocking connect, since the connection
+ * is always local and can always be fulfilled immediately.
+ */
+
+ uintptr_t out = 0;
+
+ if (!server || !*server)
+ return 0;
+
+ sockaddr_un pun;
+ memset (&pun, 0, sizeof(pun));
+ pun.sun_family = AF_LOCAL;
+
+ // You ordinarily expect the server name field to be at least 1024 bytes long,
+ // but on Linux it can be MUCH shorter.
+ if (strlen(server) >= sizeof(pun.sun_path))
+ throw std::runtime_error ("unix-domain server name is too long");
+
+
+ strcpy (pun.sun_path, server);
+
+ SOCKET fd = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
+ if (fd == INVALID_SOCKET)
+ return 0;
+
+ // From here on, ALL error returns must close the socket.
+ // NOTE: At this point, the socket is still a blocking socket.
+ if (connect (fd, (struct sockaddr*)&pun, sizeof(pun)) != 0) {
+ close (fd);
+ return 0;
+ }
+
+ // Set the newly-connected socket nonblocking.
+ if (!SetSocketNonblocking (fd)) {
+ close (fd);
+ return 0;
+ }
+
+ // Set up a connection descriptor and add it to the event-machine.
+ // Observe, even though we know the connection status is connect-success,
+ // we still set the "pending" flag, so some needed initializations take
+ // place.
+ ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+ cd->SetConnectPending (true);
+ Add (cd);
+ out = cd->GetBinding();
+
+ if (!out)
+ close (fd);
+
+ return out;
+}
+#else
+const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server UNUSED)
+{
+ throw std::runtime_error ("unix-domain connection unavailable on this platform");
+}
+#endif
+
+/************************
+EventMachine_t::AttachFD
+************************/
+
+const uintptr_t EventMachine_t::AttachFD (SOCKET fd, bool watch_mode)
+{
+ #ifdef OS_UNIX
+ if (fcntl(fd, F_GETFL, 0) < 0) {
+ if (errno) {
+ throw std::runtime_error (strerror(errno));
+ } else {
+ throw std::runtime_error ("invalid file descriptor");
+ }
+ }
+ #endif
+
+ #ifdef OS_WIN32
+ // TODO: add better check for invalid file descriptors (see ioctlsocket or getsockopt)
+ if (fd == INVALID_SOCKET)
+ throw std::runtime_error ("invalid file descriptor");
+ #endif
+
+ {// Check for duplicate descriptors
+ size_t i;
+ for (i = 0; i < Descriptors.size(); i++) {
+ EventableDescriptor *ed = Descriptors[i];
+ assert (ed);
+ if (ed->GetSocket() == fd)
+ throw std::runtime_error ("adding existing descriptor");
+ }
+
+ for (i = 0; i < NewDescriptors.size(); i++) {
+ EventableDescriptor *ed = NewDescriptors[i];
+ assert (ed);
+ if (ed->GetSocket() == fd)
+ throw std::runtime_error ("adding existing new descriptor");
+ }
+ }
+
+ if (!watch_mode)
+ SetSocketNonblocking(fd);
+
+ ConnectionDescriptor *cd = new ConnectionDescriptor (fd, this);
+ if (!cd)
+ throw std::runtime_error ("no connection allocated");
+
+ cd->SetAttached(true);
+ cd->SetWatchOnly(watch_mode);
+ cd->SetConnectPending (false);
+
+ Add (cd);
+
+ const uintptr_t out = cd->GetBinding();
+ return out;
+}
+
+/************************
+EventMachine_t::DetachFD
+************************/
+
+int EventMachine_t::DetachFD (EventableDescriptor *ed)
+{
+ if (!ed)
+ throw std::runtime_error ("detaching bad descriptor");
+
+ SOCKET fd = ed->GetSocket();
+
+ #ifdef HAVE_EPOLL
+ if (Poller == Poller_Epoll) {
+ if (ed->GetSocket() != INVALID_SOCKET) {
+ assert (epfd != -1);
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
+ if (e && (errno != ENOENT) && (errno != EBADF)) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+ }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (Poller == Poller_Kqueue) {
+ // remove any read/write events for this fd
+ struct kevent k;
+#ifdef __NetBSD__
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, (intptr_t)ed);
+#else
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ | EVFILT_WRITE, EV_DELETE, 0, 0, ed);
+#endif
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+ if (t < 0 && (errno != ENOENT) && (errno != EBADF)) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to delete kqueue event: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+ #endif
+
+ // Prevent the descriptor from being modified, in case DetachFD was called from a timer or next_tick
+ ModifiedDescriptors.erase (ed);
+
+ // Prevent the descriptor from being added, in case DetachFD was called in the same tick as AttachFD
+ for (size_t i = 0; i < NewDescriptors.size(); i++) {
+ if (ed == NewDescriptors[i]) {
+ NewDescriptors.erase(NewDescriptors.begin() + i);
+ break;
+ }
+ }
+
+ // Set MySocket = INVALID_SOCKET so ShouldDelete() is true (and the descriptor gets deleted and removed),
+ // and also to prevent anyone from calling close() on the detached fd
+ ed->SetSocketInvalid();
+
+ return fd;
+}
+
+/************
+name2address
+************/
+
+int EventMachine_t::name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len)
+{
+ if (!server || !*server)
+ server = "0.0.0.0";
+
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_socktype = socktype;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
+
+ char portstr[12];
+ snprintf(portstr, sizeof(portstr), "%u", port);
+
+ int gai = getaddrinfo (server, portstr, &hints, &ai);
+ if (gai == 0) {
+ assert (ai->ai_addrlen <= *addr_len);
+ memcpy (addr, ai->ai_addr, ai->ai_addrlen);
+ *addr_len = ai->ai_addrlen;
+ freeaddrinfo(ai);
+ }
+
+ return gai;
+}
+
+
+/*******************************
+EventMachine_t::CreateTcpServer
+*******************************/
+
+const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port)
+{
+ /* Create a TCP-acceptor (server) socket and add it to the event machine.
+ * Return the binding of the new acceptor to the caller.
+ * This binding will be referenced when the new acceptor sends events
+ * to indicate accepted connections.
+ */
+
+
+ struct sockaddr_storage bind_here;
+ size_t bind_here_len = sizeof bind_here;
+ if (0 != name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_here, &bind_here_len))
+ return 0;
+
+ SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0);
+ if (sd_accept == INVALID_SOCKET) {
+ goto fail;
+ }
+
+ { // set reuseaddr to improve performance on restarts.
+ int oval = 1;
+ if (setsockopt (sd_accept, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) {
+ //__warning ("setsockopt failed while creating listener","");
+ goto fail;
+ }
+ }
+
+ { // set CLOEXEC. Only makes sense on Unix
+ #ifdef OS_UNIX
+ int cloexec = fcntl (sd_accept, F_GETFD, 0);
+ assert (cloexec >= 0);
+ cloexec |= FD_CLOEXEC;
+ fcntl (sd_accept, F_SETFD, cloexec);
+ #endif
+ }
+
+
+ if (bind (sd_accept, (struct sockaddr *)&bind_here, bind_here_len)) {
+ //__warning ("binding failed");
+ goto fail;
+ }
+
+ if (listen (sd_accept, 100)) {
+ //__warning ("listen failed");
+ goto fail;
+ }
+
+ return AttachSD(sd_accept);
+
+ fail:
+ if (sd_accept != INVALID_SOCKET)
+ close (sd_accept);
+ return 0;
+}
+
+
+/**********************************
+EventMachine_t::OpenDatagramSocket
+**********************************/
+
+const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int port)
+{
+ uintptr_t output_binding = 0;
+
+ struct sockaddr_storage bind_here;
+ size_t bind_here_len = sizeof bind_here;
+ if (0 != name2address (address, port, SOCK_DGRAM, (struct sockaddr *)&bind_here, &bind_here_len))
+ return 0;
+
+ // from here on, early returns must close the socket!
+ SOCKET sd = EmSocket (bind_here.ss_family, SOCK_DGRAM, 0);
+ if (sd == INVALID_SOCKET)
+ goto fail;
+
+ { // set the SO_REUSEADDR on the socket before we bind, otherwise it won't work for a second one
+ int oval = 1;
+ if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0)
+ goto fail;
+ }
+
+ // Set the new socket nonblocking.
+ if (!SetSocketNonblocking (sd))
+ goto fail;
+
+ if (bind (sd, (struct sockaddr *)&bind_here, bind_here_len) != 0)
+ goto fail;
+
+ { // Looking good.
+ DatagramDescriptor *ds = new DatagramDescriptor (sd, this);
+ if (!ds)
+ throw std::runtime_error ("unable to allocate datagram-socket");
+ Add (ds);
+ output_binding = ds->GetBinding();
+ }
+
+ return output_binding;
+
+ fail:
+ if (sd != INVALID_SOCKET)
+ close (sd);
+ return 0;
+}
+
+
+
+/*******************
+EventMachine_t::Add
+*******************/
+
+void EventMachine_t::Add (EventableDescriptor *ed)
+{
+ if (!ed)
+ throw std::runtime_error ("added bad descriptor");
+ ed->SetEventCallback (EventCallback);
+ NewDescriptors.push_back (ed);
+}
+
+
+/*******************************
+EventMachine_t::ArmKqueueWriter
+*******************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed)
+{
+ if (Poller == Poller_Kqueue) {
+ if (!ed)
+ throw std::runtime_error ("added bad descriptor");
+ struct kevent k;
+#ifdef __NetBSD__
+ EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, (intptr_t)ed);
+#else
+ EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, ed);
+#endif
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+ if (t < 0) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "arm kqueue writer failed on %d: %s", ed->GetSocket(), strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+}
+#else
+void EventMachine_t::ArmKqueueWriter (EventableDescriptor *ed UNUSED) { }
+#endif
+
+/*******************************
+EventMachine_t::ArmKqueueReader
+*******************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed)
+{
+ if (Poller == Poller_Kqueue) {
+ if (!ed)
+ throw std::runtime_error ("added bad descriptor");
+ struct kevent k;
+#ifdef __NetBSD__
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, (intptr_t)ed);
+#else
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
+#endif
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+ if (t < 0) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "arm kqueue reader failed on %d: %s", ed->GetSocket(), strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+}
+#else
+void EventMachine_t::ArmKqueueReader (EventableDescriptor *ed UNUSED) { }
+#endif
+
+/**********************************
+EventMachine_t::_AddNewDescriptors
+**********************************/
+
+void EventMachine_t::_AddNewDescriptors()
+{
+ /* Avoid adding descriptors to the main descriptor list
+ * while we're actually traversing the list.
+ * Any descriptors that are added as a result of processing timers
+ * or acceptors should go on a temporary queue and then added
+ * while we're not traversing the main list.
+ * Also, it (rarely) happens that a newly-created descriptor
+ * is immediately scheduled to close. It might be a good
+ * idea not to bother scheduling these for I/O but if
+ * we do that, we might bypass some important processing.
+ */
+
+ for (size_t i = 0; i < NewDescriptors.size(); i++) {
+ EventableDescriptor *ed = NewDescriptors[i];
+ if (ed == NULL)
+ throw std::runtime_error ("adding bad descriptor");
+
+ #if HAVE_EPOLL
+ if (Poller == Poller_Epoll) {
+ assert (epfd != -1);
+ int e = epoll_ctl (epfd, EPOLL_CTL_ADD, ed->GetSocket(), ed->GetEpollEvent());
+ if (e) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to add new descriptor: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ }
+ #endif
+
+ #if HAVE_KQUEUE
+ /*
+ if (Poller == Poller_Kqueue) {
+ // INCOMPLETE. Some descriptors don't want to be readable.
+ assert (kqfd != -1);
+ struct kevent k;
+#ifdef __NetBSD__
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, (intptr_t)ed);
+#else
+ EV_SET (&k, ed->GetSocket(), EVFILT_READ, EV_ADD, 0, 0, ed);
+#endif
+ int t = kevent (kqfd, &k, 1, NULL, 0, NULL);
+ assert (t == 0);
+ }
+ */
+ #endif
+
+ QueueHeartbeat(ed);
+ Descriptors.push_back (ed);
+ }
+ NewDescriptors.clear();
+}
+
+
+/**********************************
+EventMachine_t::_ModifyDescriptors
+**********************************/
+
+void EventMachine_t::_ModifyDescriptors()
+{
+ /* For implementations which don't level check every descriptor on
+ * every pass through the machine, as select does.
+ * If we're not selecting, then descriptors need a way to signal to the
+ * machine that their readable or writable status has changed.
+ * That's what the ::Modify call is for. We do it this way to avoid
+ * modifying descriptors during the loop traversal, where it can easily
+ * happen that an object (like a UDP socket) gets data written on it by
+ * the application during #post_init. That would take place BEFORE the
+ * descriptor even gets added to the epoll descriptor, so the modify
+ * operation will crash messily.
+ * Another really messy possibility is for a descriptor to put itself
+ * on the Modified list, and then get deleted before we get here.
+ * Remember, deletes happen after the I/O traversal and before the
+ * next pass through here. So we have to make sure when we delete a
+ * descriptor to remove it from the Modified list.
+ */
+
+ #ifdef HAVE_EPOLL
+ if (Poller == Poller_Epoll) {
+ std::set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
+ while (i != ModifiedDescriptors.end()) {
+ assert (*i);
+ _ModifyEpollEvent (*i);
+ ++i;
+ }
+ }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (Poller == Poller_Kqueue) {
+ std::set<EventableDescriptor*>::iterator i = ModifiedDescriptors.begin();
+ while (i != ModifiedDescriptors.end()) {
+ assert (*i);
+ if ((*i)->GetKqueueArmWrite())
+ ArmKqueueWriter (*i);
+ ++i;
+ }
+ }
+ #endif
+
+ ModifiedDescriptors.clear();
+}
+
+
+/**********************
+EventMachine_t::Modify
+**********************/
+
+void EventMachine_t::Modify (EventableDescriptor *ed)
+{
+ if (!ed)
+ throw std::runtime_error ("modified bad descriptor");
+ ModifiedDescriptors.insert (ed);
+}
+
+
+/***********************
+EventMachine_t::Deregister
+***********************/
+
+void EventMachine_t::Deregister (EventableDescriptor *ed)
+{
+ if (!ed)
+ throw std::runtime_error ("modified bad descriptor");
+ #ifdef HAVE_EPOLL
+ // cut/paste from _CleanupSockets(). The error handling could be
+ // refactored out of there, but it is cut/paste all over the
+ // file already.
+ if (Poller == Poller_Epoll) {
+ assert (epfd != -1);
+ assert (ed->GetSocket() != INVALID_SOCKET);
+ int e = epoll_ctl (epfd, EPOLL_CTL_DEL, ed->GetSocket(), ed->GetEpollEvent());
+ // ENOENT or EBADF are not errors because the socket may be already closed when we get here.
+ if (e && (errno != ENOENT) && (errno != EBADF) && (errno != EPERM)) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "unable to delete epoll event: %s", strerror(errno));
+ throw std::runtime_error (buf);
+ }
+ ModifiedDescriptors.erase(ed);
+ }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (Poller == Poller_Kqueue) {
+ assert (ed->GetSocket() != INVALID_SOCKET);
+
+ ModifiedDescriptors.erase(ed);
+ }
+ #endif
+}
+
+
+/**************************************
+EventMachine_t::CreateUnixDomainServer
+**************************************/
+
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename)
+{
+ /* Create a UNIX-domain acceptor (server) socket and add it to the event machine.
+ * Return the binding of the new acceptor to the caller.
+ * This binding will be referenced when the new acceptor sends events
+ * to indicate accepted connections.
+ * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS.
+ */
+
+ struct sockaddr_un s_sun;
+
+ SOCKET sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0);
+ if (sd_accept == INVALID_SOCKET) {
+ goto fail;
+ }
+
+ if (!filename || !*filename)
+ goto fail;
+ unlink (filename);
+
+ bzero (&s_sun, sizeof(s_sun));
+ s_sun.sun_family = AF_LOCAL;
+ strncpy (s_sun.sun_path, filename, sizeof(s_sun.sun_path)-1);
+
+ // don't bother with reuseaddr for a local socket.
+
+ { // set CLOEXEC. Only makes sense on Unix
+ #ifdef OS_UNIX
+ int cloexec = fcntl (sd_accept, F_GETFD, 0);
+ assert (cloexec >= 0);
+ cloexec |= FD_CLOEXEC;
+ fcntl (sd_accept, F_SETFD, cloexec);
+ #endif
+ }
+
+ if (bind (sd_accept, (struct sockaddr*)&s_sun, sizeof(s_sun))) {
+ //__warning ("binding failed");
+ goto fail;
+ }
+
+ if (listen (sd_accept, 100)) {
+ //__warning ("listen failed");
+ goto fail;
+ }
+
+ return AttachSD(sd_accept);
+
+ fail:
+ if (sd_accept != INVALID_SOCKET)
+ close (sd_accept);
+ return 0;
+}
+#else
+const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename UNUSED)
+{
+ throw std::runtime_error ("unix-domain server unavailable on this platform");
+}
+#endif
+
+
+/**************************************
+EventMachine_t::AttachSD
+**************************************/
+
+const uintptr_t EventMachine_t::AttachSD (SOCKET sd_accept)
+{
+ uintptr_t output_binding = 0;
+
+ {
+ // Set the acceptor non-blocking.
+ // THIS IS CRUCIALLY IMPORTANT because we read it in a select loop.
+ if (!SetSocketNonblocking (sd_accept)) {
+ //int val = fcntl (sd_accept, F_GETFL, 0);
+ //if (fcntl (sd_accept, F_SETFL, val | O_NONBLOCK) == -1) {
+ goto fail;
+ }
+ }
+
+ { // Looking good.
+ AcceptorDescriptor *ad = new AcceptorDescriptor (sd_accept, this);
+ if (!ad)
+ throw std::runtime_error ("unable to allocate acceptor");
+ Add (ad);
+ output_binding = ad->GetBinding();
+ }
+
+ return output_binding;
+
+ fail:
+ if (sd_accept != INVALID_SOCKET)
+ close (sd_accept);
+ return 0;
+}
+
+
+/**************************
+EventMachine_t::Socketpair
+**************************/
+
+#ifdef OS_UNIX
+const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings)
+{
+ // Make sure the incoming array of command strings is sane.
+ if (!cmd_strings)
+ return 0;
+ int j;
+ for (j=0; j < 2048 && cmd_strings[j]; j++)
+ ;
+ if ((j==0) || (j==2048))
+ return 0;
+
+ uintptr_t output_binding = 0;
+
+ int sv[2];
+ if (socketpair (AF_LOCAL, SOCK_STREAM, 0, sv) < 0)
+ return 0;
+ // from here, all early returns must close the pair of sockets.
+
+ // Set the parent side of the socketpair nonblocking.
+ // We don't care about the child side, and most child processes will expect their
+ // stdout to be blocking. Thanks to Duane Johnson and Bill Kelly for pointing this out.
+ // Obviously DON'T set CLOEXEC.
+ if (!SetSocketNonblocking (sv[0])) {
+ close (sv[0]);
+ close (sv[1]);
+ return 0;
+ }
+
+ pid_t f = fork();
+ if (f > 0) {
+ close (sv[1]);
+ PipeDescriptor *pd = new PipeDescriptor (sv[0], f, this);
+ if (!pd)
+ throw std::runtime_error ("unable to allocate pipe");
+ Add (pd);
+ output_binding = pd->GetBinding();
+ }
+ else if (f == 0) {
+ close (sv[0]);
+ dup2 (sv[1], STDIN_FILENO);
+ close (sv[1]);
+ dup2 (STDIN_FILENO, STDOUT_FILENO);
+ execvp (cmd_strings[0], cmd_strings+1);
+ exit (-1); // end the child process if the exec doesn't work.
+ }
+ else
+ throw std::runtime_error ("no fork");
+
+ return output_binding;
+}
+#else
+const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings UNUSED)
+{
+ throw std::runtime_error ("socketpair is currently unavailable on this platform");
+}
+#endif
+
+
+
+/****************************
+EventMachine_t::OpenKeyboard
+****************************/
+
+const uintptr_t EventMachine_t::OpenKeyboard()
+{
+ KeyboardDescriptor *kd = new KeyboardDescriptor (this);
+ if (!kd)
+ throw std::runtime_error ("no keyboard-object allocated");
+ Add (kd);
+ return kd->GetBinding();
+}
+
+
+/**********************************
+EventMachine_t::GetConnectionCount
+**********************************/
+
+int EventMachine_t::GetConnectionCount ()
+{
+ int i = 0;
+ // Subtract one for epoll or kqueue because of the LoopbreakDescriptor
+ if (Poller == Poller_Epoll || Poller == Poller_Kqueue)
+ i = 1;
+
+ return Descriptors.size() + NewDescriptors.size() - i;
+}
+
+
+/************************
+EventMachine_t::WatchPid
+************************/
+
+#ifdef HAVE_KQUEUE
+const uintptr_t EventMachine_t::WatchPid (int pid)
+{
+ if (Poller != Poller_Kqueue)
+ throw std::runtime_error("must enable kqueue (EM.kqueue=true) for pid watching support");
+
+ struct kevent event;
+ int kqres;
+
+ EV_SET(&event, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT | NOTE_FORK, 0, 0);
+
+ // Attempt to register the event
+ kqres = kevent(kqfd, &event, 1, NULL, 0, NULL);
+ if (kqres == -1) {
+ char errbuf[200];
+ sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
+ throw std::runtime_error(errbuf);
+ }
+ Bindable_t* b = new Bindable_t();
+ Pids.insert(std::make_pair (pid, b));
+
+ return b->GetBinding();
+}
+#else
+const uintptr_t EventMachine_t::WatchPid (int pid UNUSED)
+{
+ throw std::runtime_error("no pid watching support on this system");
+}
+#endif
+
+/**************************
+EventMachine_t::UnwatchPid
+**************************/
+
+void EventMachine_t::UnwatchPid (int pid)
+{
+ Bindable_t *b = Pids[pid];
+ assert(b);
+ Pids.erase(pid);
+
+ #ifdef HAVE_KQUEUE
+ struct kevent k;
+
+ EV_SET(&k, pid, EVFILT_PROC, EV_DELETE, 0, 0, 0);
+ /*int t =*/ kevent (kqfd, &k, 1, NULL, 0, NULL);
+ // t==-1 if the process already exited; ignore this for now
+ #endif
+
+ if (EventCallback)
+ (*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
+
+ delete b;
+}
+
+void EventMachine_t::UnwatchPid (const uintptr_t sig)
+{
+ for(std::map<int, Bindable_t*>::iterator i=Pids.begin(); i != Pids.end(); i++)
+ {
+ if (i->second->GetBinding() == sig) {
+ UnwatchPid (i->first);
+ return;
+ }
+ }
+
+ throw std::runtime_error("attempted to remove invalid pid signature");
+}
+
+
+/*************************
+EventMachine_t::WatchFile
+*************************/
+
+const uintptr_t EventMachine_t::WatchFile (const char *fpath)
+{
+ struct stat sb;
+ int sres;
+ int wd = -1;
+
+ sres = stat(fpath, &sb);
+
+ if (sres == -1) {
+ char errbuf[300];
+ sprintf(errbuf, "error registering file %s for watching: %s", fpath, strerror(errno));
+ throw std::runtime_error(errbuf);
+ }
+
+ #ifdef HAVE_INOTIFY
+ if (!inotify) {
+ inotify = new InotifyDescriptor(this);
+ assert (inotify);
+ Add(inotify);
+ }
+
+ wd = inotify_add_watch(inotify->GetSocket(), fpath,
+ IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_MOVE) ;
+ if (wd == -1) {
+ char errbuf[300];
+ sprintf(errbuf, "failed to open file %s for registering with inotify: %s", fpath, strerror(errno));
+ throw std::runtime_error(errbuf);
+ }
+ #endif
+
+ #ifdef HAVE_KQUEUE
+ if (Poller != Poller_Kqueue)
+ throw std::runtime_error("must enable kqueue (EM.kqueue=true) for file watching support");
+
+ // With kqueue we have to open the file first and use the resulting fd to register for events
+ wd = open(fpath, O_RDONLY);
+ if (wd == -1) {
+ char errbuf[300];
+ sprintf(errbuf, "failed to open file %s for registering with kqueue: %s", fpath, strerror(errno));
+ throw std::runtime_error(errbuf);
+ }
+ _RegisterKqueueFileEvent(wd);
+ #endif
+
+ if (wd != -1) {
+ Bindable_t* b = new Bindable_t();
+ Files.insert(std::make_pair (wd, b));
+
+ return b->GetBinding();
+ }
+
+ throw std::runtime_error("no file watching support on this system"); // is this the right thing to do?
+}
+
+
+/***************************
+EventMachine_t::UnwatchFile
+***************************/
+
+void EventMachine_t::UnwatchFile (int wd)
+{
+ Bindable_t *b = Files[wd];
+ assert(b);
+ Files.erase(wd);
+
+ #ifdef HAVE_INOTIFY
+ inotify_rm_watch(inotify->GetSocket(), wd);
+ #elif HAVE_KQUEUE
+ // With kqueue, closing the monitored fd automatically clears all registered events for it
+ close(wd);
+ #endif
+
+ if (EventCallback)
+ (*EventCallback)(b->GetBinding(), EM_CONNECTION_UNBOUND, NULL, 0);
+
+ delete b;
+}
+
+void EventMachine_t::UnwatchFile (const uintptr_t sig)
+{
+ for(std::map<int, Bindable_t*>::iterator i=Files.begin(); i != Files.end(); i++)
+ {
+ if (i->second->GetBinding() == sig) {
+ UnwatchFile (i->first);
+ return;
+ }
+ }
+ throw std::runtime_error("attempted to remove invalid watch signature");
+}
+
+
+/***********************************
+EventMachine_t::_ReadInotify_Events
+************************************/
+
+void EventMachine_t::_ReadInotifyEvents()
+{
+ #ifdef HAVE_INOTIFY
+ char buffer[1024];
+
+ assert(EventCallback);
+
+ for (;;) {
+ int returned = read(inotify->GetSocket(), buffer, sizeof(buffer));
+ assert(!(returned == 0 || (returned == -1 && errno == EINVAL)));
+ if (returned <= 0) {
+ break;
+ }
+ int current = 0;
+ while (current < returned) {
+ struct inotify_event* event = (struct inotify_event*)(buffer+current);
+ std::map<int, Bindable_t*>::const_iterator bindable = Files.find(event->wd);
+ if (bindable != Files.end()) {
+ if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "modified", 8);
+ }
+ if (event->mask & IN_MOVE_SELF){
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "moved", 5);
+ }
+ if (event->mask & IN_DELETE_SELF) {
+ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
+ UnwatchFile ((int)event->wd);
+ }
+ }
+ current += sizeof(struct inotify_event) + event->len;
+ }
+ }
+ #endif
+}
+
+
+/*************************************
+EventMachine_t::_HandleKqueuePidEvent
+*************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_HandleKqueuePidEvent(struct kevent *event)
+{
+ assert(EventCallback);
+
+ if (event->fflags & NOTE_FORK)
+ (*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "fork", 4);
+ if (event->fflags & NOTE_EXIT) {
+ (*EventCallback)(Pids [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "exit", 4);
+ // stop watching the pid if it died
+ UnwatchPid ((int)event->ident);
+ }
+}
+#endif
+
+
+/**************************************
+EventMachine_t::_HandleKqueueFileEvent
+***************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_HandleKqueueFileEvent(struct kevent *event)
+{
+ assert(EventCallback);
+
+ if (event->fflags & NOTE_WRITE)
+ (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "modified", 8);
+ if (event->fflags & NOTE_RENAME)
+ (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "moved", 5);
+ if (event->fflags & NOTE_DELETE) {
+ (*EventCallback)(Files [(int) event->ident]->GetBinding(), EM_CONNECTION_READ, "deleted", 7);
+ UnwatchFile ((int)event->ident);
+ }
+}
+#endif
+
+
+/****************************************
+EventMachine_t::_RegisterKqueueFileEvent
+*****************************************/
+
+#ifdef HAVE_KQUEUE
+void EventMachine_t::_RegisterKqueueFileEvent(int fd)
+{
+ struct kevent newevent;
+ int kqres;
+
+ // Setup the event with our fd and proper flags
+ EV_SET(&newevent, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE, 0, 0);
+
+ // Attempt to register the event
+ kqres = kevent(kqfd, &newevent, 1, NULL, 0, NULL);
+ if (kqres == -1) {
+ char errbuf[200];
+ sprintf(errbuf, "failed to register file watch descriptor with kqueue: %s", strerror(errno));
+ close(fd);
+ throw std::runtime_error(errbuf);
+ }
+}
+#endif
+
+
+/************************************
+EventMachine_t::GetHeartbeatInterval
+*************************************/
+
+float EventMachine_t::GetHeartbeatInterval()
+{
+ return ((float)HeartbeatInterval / 1000000);
+}
+
+
+/************************************
+EventMachine_t::SetHeartbeatInterval
+*************************************/
+
+int EventMachine_t::SetHeartbeatInterval(float interval)
+{
+ int iv = (int)(interval * 1000000);
+ if (iv > 0) {
+ HeartbeatInterval = iv;
+ return 1;
+ }
+ return 0;
+}
+//#endif // OS_UNIX
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.h
new file mode 100644
index 0000000..874a127
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/em.h
@@ -0,0 +1,308 @@
+/*****************************************************************************
+
+$Id$
+
+File: em.h
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+#ifndef __EventMachine__H_
+#define __EventMachine__H_
+
+#ifdef BUILD_FOR_RUBY
+ #include <ruby.h>
+ #ifdef HAVE_RB_THREAD_FD_SELECT
+ #define EmSelect rb_thread_fd_select
+ #else
+ // ruby 1.9.1 and below
+ #define EmSelect rb_thread_select
+ #endif
+
+ #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+ #include <ruby/thread.h>
+ #endif
+
+ #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD
+ #include <ruby/io.h>
+ #endif
+
+ #if defined(HAVE_RB_TRAP_IMMEDIATE)
+ #include <rubysig.h>
+ #elif defined(HAVE_RB_ENABLE_INTERRUPT)
+ extern "C" {
+ void rb_enable_interrupt(void);
+ void rb_disable_interrupt(void);
+ }
+
+ #define TRAP_BEG rb_enable_interrupt()
+ #define TRAP_END do { rb_disable_interrupt(); rb_thread_check_ints(); } while(0)
+ #else
+ #define TRAP_BEG
+ #define TRAP_END
+ #endif
+
+ // 1.9.0 compat
+ #ifndef RUBY_UBF_IO
+ #define RUBY_UBF_IO RB_UBF_DFL
+ #endif
+ #ifndef RSTRING_PTR
+ #define RSTRING_PTR(str) RSTRING(str)->ptr
+ #endif
+ #ifndef RSTRING_LEN
+ #define RSTRING_LEN(str) RSTRING(str)->len
+ #endif
+ #ifndef RSTRING_LENINT
+ #define RSTRING_LENINT(str) RSTRING_LEN(str)
+ #endif
+#else
+ #define EmSelect select
+#endif
+
+#if !defined(HAVE_TYPE_RB_FDSET_T)
+#define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n)))
+// These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3,
+// with this change: any macros that read or write the nth element of an
+// fdset first call fd_check to make sure n is in bounds.
+typedef fd_set rb_fdset_t;
+#define rb_fd_zero(f) FD_ZERO(f)
+#define rb_fd_set(n, f) do { if (fd_check(n)) FD_SET((n), (f)); } while(0)
+#define rb_fd_clr(n, f) do { if (fd_check(n)) FD_CLR((n), (f)); } while(0)
+#define rb_fd_isset(n, f) (fd_check(n) ? FD_ISSET((n), (f)) : 0)
+#define rb_fd_copy(d, s, n) (*(d) = *(s))
+#define rb_fd_dup(d, s) (*(d) = *(s))
+#define rb_fd_resize(n, f) ((void)(f))
+#define rb_fd_ptr(f) (f)
+#define rb_fd_init(f) FD_ZERO(f)
+#define rb_fd_init_copy(d, s) (*(d) = *(s))
+#define rb_fd_term(f) ((void)(f))
+#define rb_fd_max(f) FD_SETSIZE
+#define rb_fd_select(n, rfds, wfds, efds, timeout) \
+ select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
+#define rb_thread_fd_select(n, rfds, wfds, efds, timeout) \
+ rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout))
+#endif
+
+
+// This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3:
+// Solaris sys/select.h switches select to select_large_fdset to support larger
+// file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment.
+// But Ruby doesn't change FD_SETSIZE because fd_set is allocated dynamically.
+// So following definition is required to use select_large_fdset.
+#ifdef HAVE_SELECT_LARGE_FDSET
+#define select(n, r, w, e, t) select_large_fdset((n), (r), (w), (e), (t))
+extern "C" {
+ int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+}
+#endif
+
+class EventableDescriptor;
+class InotifyDescriptor;
+struct SelectData_t;
+
+/*************
+enum Poller_t
+*************/
+enum Poller_t {
+ Poller_Default, // typically Select
+ Poller_Epoll,
+ Poller_Kqueue
+};
+
+
+/********************
+class EventMachine_t
+********************/
+
+class EventMachine_t
+{
+ public:
+ static int GetMaxTimerCount();
+ static void SetMaxTimerCount (int);
+
+ static int GetSimultaneousAcceptCount();
+ static void SetSimultaneousAcceptCount (int);
+
+ public:
+ EventMachine_t (EMCallback, Poller_t);
+ virtual ~EventMachine_t();
+
+ bool RunOnce();
+ void Run();
+ void ScheduleHalt();
+ bool Stopping();
+ void SignalLoopBreaker();
+ const uintptr_t InstallOneshotTimer (uint64_t);
+ const uintptr_t ConnectToServer (const char *, int, const char *, int);
+ const uintptr_t ConnectToUnixServer (const char *);
+
+ const uintptr_t CreateTcpServer (const char *, int);
+ const uintptr_t OpenDatagramSocket (const char *, int);
+ const uintptr_t CreateUnixDomainServer (const char*);
+ const uintptr_t AttachSD (SOCKET);
+ const uintptr_t OpenKeyboard();
+ //const char *Popen (const char*, const char*);
+ const uintptr_t Socketpair (char* const*);
+
+ void Add (EventableDescriptor*);
+ void Modify (EventableDescriptor*);
+ void Deregister (EventableDescriptor*);
+
+ const uintptr_t AttachFD (SOCKET, bool);
+ int DetachFD (EventableDescriptor*);
+
+ void ArmKqueueWriter (EventableDescriptor*);
+ void ArmKqueueReader (EventableDescriptor*);
+
+ void SetTimerQuantum (int);
+ static void SetuidString (const char*);
+ static int SetRlimitNofile (int);
+
+ pid_t SubprocessPid;
+ int SubprocessExitStatus;
+
+ int GetConnectionCount();
+ float GetHeartbeatInterval();
+ int SetHeartbeatInterval(float);
+
+ const uintptr_t WatchFile (const char*);
+ void UnwatchFile (int);
+ void UnwatchFile (const uintptr_t);
+
+ #ifdef HAVE_KQUEUE
+ void _HandleKqueueFileEvent (struct kevent*);
+ void _RegisterKqueueFileEvent(int);
+ #endif
+
+ const uintptr_t WatchPid (int);
+ void UnwatchPid (int);
+ void UnwatchPid (const uintptr_t);
+
+ #ifdef HAVE_KQUEUE
+ void _HandleKqueuePidEvent (struct kevent*);
+ #endif
+
+ uint64_t GetCurrentLoopTime() { return MyCurrentLoopTime; }
+
+ void QueueHeartbeat(EventableDescriptor*);
+ void ClearHeartbeat(uint64_t, EventableDescriptor*);
+
+ uint64_t GetRealTime();
+
+ Poller_t GetPoller() { return Poller; }
+
+ static int name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len);
+
+ private:
+ void _RunTimers();
+ void _UpdateTime();
+ void _AddNewDescriptors();
+ void _ModifyDescriptors();
+ void _InitializeLoopBreaker();
+ void _CleanupSockets();
+
+ void _RunSelectOnce();
+ void _RunEpollOnce();
+ void _RunKqueueOnce();
+
+ void _ModifyEpollEvent (EventableDescriptor*);
+ void _DispatchHeartbeats();
+ timeval _TimeTilNextEvent();
+ void _CleanBadDescriptors();
+
+ public:
+ void _ReadLoopBreaker();
+ void _ReadInotifyEvents();
+ int NumCloseScheduled;
+
+ private:
+ enum {
+ MaxEpollDescriptors = 64*1024,
+ MaxEvents = 4096
+ };
+ int HeartbeatInterval;
+ EMCallback EventCallback;
+
+ class Timer_t: public Bindable_t {
+ };
+
+ std::multimap<uint64_t, Timer_t> Timers;
+ std::multimap<uint64_t, EventableDescriptor*> Heartbeats;
+ std::map<int, Bindable_t*> Files;
+ std::map<int, Bindable_t*> Pids;
+ std::vector<EventableDescriptor*> Descriptors;
+ std::vector<EventableDescriptor*> NewDescriptors;
+ std::set<EventableDescriptor*> ModifiedDescriptors;
+
+ SOCKET LoopBreakerReader;
+ SOCKET LoopBreakerWriter;
+ #ifdef OS_WIN32
+ struct sockaddr_in LoopBreakerTarget;
+ #endif
+
+ timeval Quantum;
+
+ uint64_t MyCurrentLoopTime;
+
+ #ifdef OS_WIN32
+ unsigned TickCountTickover;
+ unsigned LastTickCount;
+ #endif
+
+ #ifdef OS_DARWIN
+ mach_timebase_info_data_t mach_timebase;
+ #endif
+
+ private:
+ bool bTerminateSignalReceived;
+ SelectData_t *SelectData;
+
+ Poller_t Poller;
+
+ int epfd; // Epoll file-descriptor
+ #ifdef HAVE_EPOLL
+ struct epoll_event epoll_events [MaxEvents];
+ #endif
+
+ int kqfd; // Kqueue file-descriptor
+ #ifdef HAVE_KQUEUE
+ struct kevent Karray [MaxEvents];
+ #endif
+
+ #ifdef HAVE_INOTIFY
+ InotifyDescriptor *inotify; // pollable descriptor for our inotify instance
+ #endif
+};
+
+
+/*******************
+struct SelectData_t
+*******************/
+
+struct SelectData_t
+{
+ SelectData_t();
+ ~SelectData_t();
+
+ int _Select();
+ void _Clear();
+
+ SOCKET maxsocket;
+ rb_fdset_t fdreads;
+ rb_fdset_t fdwrites;
+ rb_fdset_t fderrors;
+ timeval tv;
+ int nSockets;
+};
+
+#endif // __EventMachine__H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/eventmachine.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/eventmachine.h
new file mode 100644
index 0000000..5100e20
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/eventmachine.h
@@ -0,0 +1,143 @@
+/*****************************************************************************
+
+$Id$
+
+File: eventmachine.h
+Date: 15Apr06
+
+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.
+
+*****************************************************************************/
+
+#ifndef __EVMA_EventMachine__H_
+#define __EVMA_EventMachine__H_
+
+#if __cplusplus
+extern "C" {
+#endif
+
+ enum { // Event names
+ EM_TIMER_FIRED = 100,
+ EM_CONNECTION_READ = 101,
+ EM_CONNECTION_UNBOUND = 102,
+ EM_CONNECTION_ACCEPTED = 103,
+ EM_CONNECTION_COMPLETED = 104,
+ EM_LOOPBREAK_SIGNAL = 105,
+ EM_CONNECTION_NOTIFY_READABLE = 106,
+ EM_CONNECTION_NOTIFY_WRITABLE = 107,
+ EM_SSL_HANDSHAKE_COMPLETED = 108,
+ EM_SSL_VERIFY = 109,
+ EM_PROXY_TARGET_UNBOUND = 110,
+ EM_PROXY_COMPLETED = 111
+ };
+
+ enum { // SSL/TLS Protocols
+ EM_PROTO_SSLv2 = 2,
+ EM_PROTO_SSLv3 = 4,
+ EM_PROTO_TLSv1 = 8,
+ EM_PROTO_TLSv1_1 = 16,
+ EM_PROTO_TLSv1_2 = 32
+ };
+
+ void evma_initialize_library (EMCallback);
+ bool evma_run_machine_once();
+ void evma_run_machine();
+ void evma_release_library();
+ const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds);
+ const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port);
+ const uintptr_t evma_connect_to_unix_server (const char *server);
+
+ const uintptr_t evma_attach_fd (int file_descriptor, int watch_mode);
+ int evma_detach_fd (const uintptr_t binding);
+ int evma_get_file_descriptor (const uintptr_t binding);
+ int evma_is_notify_readable (const uintptr_t binding);
+ void evma_set_notify_readable (const uintptr_t binding, int mode);
+ int evma_is_notify_writable (const uintptr_t binding);
+ void evma_set_notify_writable (const uintptr_t binding, int mode);
+
+ int evma_pause(const uintptr_t binding);
+ int evma_is_paused(const uintptr_t binding);
+ int evma_resume(const uintptr_t binding);
+
+ int evma_num_close_scheduled();
+
+ void evma_stop_tcp_server (const uintptr_t binding);
+ const uintptr_t evma_create_tcp_server (const char *address, int port);
+ const uintptr_t evma_create_unix_domain_server (const char *filename);
+ const uintptr_t evma_attach_sd (int sd);
+ const uintptr_t evma_open_datagram_socket (const char *server, int port);
+ const uintptr_t evma_open_keyboard();
+ void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols);
+ void evma_start_tls (const uintptr_t binding);
+
+ #ifdef WITH_SSL
+ X509 *evma_get_peer_cert (const uintptr_t binding);
+ int evma_get_cipher_bits (const uintptr_t binding);
+ const char *evma_get_cipher_name (const uintptr_t binding);
+ const char *evma_get_cipher_protocol (const uintptr_t binding);
+ const char *evma_get_sni_hostname (const uintptr_t binding);
+ void evma_accept_ssl_peer (const uintptr_t binding);
+ #endif
+
+ int evma_get_peername (const uintptr_t binding, struct sockaddr*, socklen_t*);
+ int evma_get_sockname (const uintptr_t binding, struct sockaddr*, socklen_t*);
+ int evma_get_subprocess_pid (const uintptr_t binding, pid_t*);
+ int evma_get_subprocess_status (const uintptr_t binding, int*);
+ int evma_get_connection_count();
+ int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length);
+ int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port);
+ float evma_get_comm_inactivity_timeout (const uintptr_t binding);
+ int evma_set_comm_inactivity_timeout (const uintptr_t binding, float value);
+ float evma_get_pending_connect_timeout (const uintptr_t binding);
+ int evma_set_pending_connect_timeout (const uintptr_t binding, float value);
+ int evma_get_outbound_data_size (const uintptr_t binding);
+ uint64_t evma_get_last_activity_time (const uintptr_t binding);
+ int evma_send_file_data_to_connection (const uintptr_t binding, const char *filename);
+
+ void evma_close_connection (const uintptr_t binding, int after_writing);
+ int evma_report_connection_error_status (const uintptr_t binding);
+ void evma_signal_loopbreak();
+ void evma_set_timer_quantum (int);
+ int evma_get_max_timer_count();
+ void evma_set_max_timer_count (int);
+ int evma_get_simultaneous_accept_count();
+ void evma_set_simultaneous_accept_count (int);
+ void evma_setuid_string (const char *username);
+ void evma_stop_machine();
+ bool evma_stopping();
+ float evma_get_heartbeat_interval();
+ int evma_set_heartbeat_interval(float);
+
+ const uintptr_t evma_popen (char * const*cmd_strings);
+
+ const uintptr_t evma_watch_filename (const char *fname);
+ void evma_unwatch_filename (const uintptr_t binding);
+
+ const uintptr_t evma_watch_pid (int);
+ void evma_unwatch_pid (const uintptr_t binding);
+
+ void evma_start_proxy(const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length);
+ void evma_stop_proxy(const uintptr_t from);
+ unsigned long evma_proxied_bytes(const uintptr_t from);
+
+ int evma_set_rlimit_nofile (int n_files);
+
+ void evma_set_epoll (int use);
+ void evma_set_kqueue (int use);
+
+ uint64_t evma_get_current_loop_time();
+#if __cplusplus
+}
+#endif
+
+
+#endif // __EventMachine__H_
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/extconf.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/extconf.rb
new file mode 100644
index 0000000..676b4c4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/extconf.rb
@@ -0,0 +1,270 @@
+require 'fileutils'
+require 'mkmf'
+
+# Eager check devs tools
+have_devel? if respond_to?(:have_devel?)
+
+def check_libs libs = [], fatal = false
+ libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
+end
+
+def check_heads heads = [], fatal = false
+ heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
+end
+
+def add_define(name)
+ $defs.push("-D#{name}")
+end
+
+##
+# OpenSSL:
+
+# override append_library, so it actually appends (instead of prepending)
+# this fixes issues with linking ssl, since libcrypto depends on symbols in libssl
+def append_library(libs, lib)
+ libs + " " + format(LIBARG, lib)
+end
+
+SSL_HEADS = %w(openssl/ssl.h openssl/err.h)
+SSL_LIBS = %w(crypto ssl)
+# OpenSSL 1.1.0 and above for Windows use the Unix library names
+# OpenSSL 0.9.8 and 1.0.x for Windows use the *eay32 library names
+SSL_LIBS_WIN = RUBY_PLATFORM =~ /mswin|mingw|bccwin/ ? %w(ssleay32 libeay32) : []
+
+def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil)
+ inc, lib = dir_config(name, idefault, ldefault)
+ if inc && lib
+ # TODO: Remove when 2.0.0 is the minimum supported version
+ # Ruby versions not incorporating the mkmf fix at
+ # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717
+ # do not properly search for lib directories, and must be corrected
+ unless lib && lib[-3, 3] == 'lib'
+ @libdir_basename = 'lib'
+ inc, lib = dir_config(name, idefault, ldefault)
+ end
+ unless idefault && ldefault
+ abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
+ abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) }
+ warn "-----\nUsing #{pretty_name} in path #{File.dirname inc}\n-----"
+ end
+ true
+ end
+end
+
+def dir_config_search(pretty_name, name, paths, &b)
+ paths.each do |p|
+ if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield
+ warn "-----\nFound #{pretty_name} in path #{p}\n-----"
+ return true
+ end
+ end
+ false
+end
+
+def pkg_config_wrapper(pretty_name, name)
+ cflags, ldflags, libs = pkg_config(name)
+ unless [cflags, ldflags, libs].any?(&:nil?) || [cflags, ldflags, libs].any?(&:empty?)
+ warn "-----\nUsing #{pretty_name} from pkg-config #{cflags} && #{ldflags} && #{libs}\n-----"
+ true
+ end
+end
+
+if ENV['CROSS_COMPILING']
+ openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e")
+ openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/")
+ if File.exist?(openssl_dir)
+ FileUtils.mkdir_p Dir.pwd+"/openssl/"
+ FileUtils.cp Dir[openssl_dir+"/include/openssl/*.h"], Dir.pwd+"/openssl/", :verbose => true
+ FileUtils.cp Dir[openssl_dir+"/lib*.a"], Dir.pwd, :verbose => true
+ $INCFLAGS << " -I#{Dir.pwd}" # for the openssl headers
+ add_define "WITH_SSL"
+ else
+ STDERR.puts
+ STDERR.puts "**************************************************************************************"
+ STDERR.puts "**** Cross-compiled OpenSSL not found"
+ STDERR.puts "**** Run: hg clone http://bitbucket.org/ged/ruby-pg && cd ruby-pg && rake openssl_libs"
+ STDERR.puts "**************************************************************************************"
+ STDERR.puts
+ end
+elsif dir_config_wrapper('OpenSSL', 'ssl')
+ # If the user has provided a --with-ssl-dir argument, we must respect it or fail.
+ add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
+elsif pkg_config_wrapper('OpenSSL', 'openssl')
+ # If we can detect OpenSSL by pkg-config, use it as the next-best option
+ add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
+elsif (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
+ # If we don't even need any options to find a usable OpenSSL, go with it
+ add_define 'WITH_SSL'
+elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do
+ (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS)
+ end
+ # Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew
+ add_define 'WITH_SSL'
+end
+
+add_define 'BUILD_FOR_RUBY'
+
+# Ruby features:
+
+have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
+have_func('rb_thread_blocking_region')
+have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
+have_func('rb_thread_fd_select')
+have_type('rb_fdset_t', 'ruby/intern.h')
+have_func('rb_wait_for_single_fd')
+have_func('rb_enable_interrupt')
+have_func('rb_time_new')
+
+# System features:
+
+add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h')
+add_define('HAVE_OLD_INOTIFY') if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h')
+have_func('writev', 'sys/uio.h')
+have_func('pipe2', 'unistd.h')
+have_func('accept4', 'sys/socket.h')
+have_const('SOCK_CLOEXEC', 'sys/socket.h')
+
+# Minor platform details between *nix and Windows:
+
+if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
+ GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
+ OS_WIN32 = true
+ add_define "OS_WIN32"
+else
+ GNU_CHAIN = true
+ OS_UNIX = true
+ add_define 'OS_UNIX'
+
+ add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
+end
+
+# Adjust number of file descriptors (FD) on Windows
+
+if RbConfig::CONFIG["host_os"] =~ /mingw/
+ found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
+ any? { |v| v.include?("FD_SETSIZE") }
+
+ add_define "FD_SETSIZE=32767" unless found
+end
+
+# Main platform invariances:
+
+case RUBY_PLATFORM
+when /mswin32/, /mingw32/, /bccwin32/
+ check_heads(%w[windows.h winsock.h], true)
+ check_libs(%w[kernel32 rpcrt4 gdi32], true)
+
+ if GNU_CHAIN
+ CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
+ else
+ $defs.push "-EHs"
+ $defs.push "-GR"
+ end
+
+ # Newer versions of Ruby already define _WIN32_WINNT, which is needed
+ # to get access to newer POSIX networking functions (e.g. getaddrinfo)
+ add_define '_WIN32_WINNT=0x0501' unless have_func('getaddrinfo')
+
+when /solaris/
+ add_define 'OS_SOLARIS8'
+ check_libs(%w[nsl socket], true)
+
+ # If Ruby was compiled for 32-bits, then select() can only handle 1024 fds
+ # There is an alternate function, select_large_fdset, that supports more.
+ have_func('select_large_fdset', 'sys/select.h')
+
+ if CONFIG['CC'] == 'cc' && (
+ `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
+ `cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
+ )
+ # SUN CHAIN
+ add_define 'CC_SUNWspro'
+ $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
+ $CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
+ CONFIG['CCDLFLAGS'] = "-KPIC"
+ CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
+ CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
+ else
+ # GNU CHAIN
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+ end
+
+when /openbsd/
+ # OpenBSD branch contributed by Guillaume Sellier.
+
+ # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
+ CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
+ CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
+
+when /darwin/
+ add_define 'OS_DARWIN'
+
+ # on Unix we need a g++ link, not gcc.
+ # Ff line contributed by Daniel Harple.
+ CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
+
+when /linux/
+ add_define 'HAVE_EPOLL' if have_func('epoll_create', 'sys/epoll.h')
+
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+when /aix/
+ CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
+
+when /cygwin/
+ # For rubies built with Cygwin, CXX may be set to CC, which is just
+ # a wrapper for gcc.
+ # This will compile, but it will not link to the C++ std library.
+ # Explicitly set CXX to use g++.
+ CONFIG['CXX'] = "g++"
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+else
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+end
+
+# Platform-specific time functions
+if have_func('clock_gettime')
+ # clock_gettime is POSIX, but the monotonic clocks are not
+ have_const('CLOCK_MONOTONIC_RAW', 'time.h') # Linux
+ have_const('CLOCK_MONOTONIC', 'time.h') # Linux, Solaris, BSDs
+else
+ have_func('gethrtime') # Older Solaris and HP-UX
+end
+
+# Hack so that try_link will test with a C++ compiler instead of a C compiler
+TRY_LINK.sub!('$(CC)', '$(CXX)')
+
+# This is our wishlist. We use whichever flags work on the host.
+# In the future, add -Werror to make sure all warnings are resolved.
+# deprecated-declarations are used in OS X OpenSSL
+# ignored-qualifiers are used by the Bindings (would-be void *)
+# unused-result because GCC 4.6 no longer silences (void) ignore_this(function)
+# address because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be
+%w(
+ -Wall
+ -Wextra
+ -Wno-deprecated-declarations
+ -Wno-ignored-qualifiers
+ -Wno-unused-result
+ -Wno-address
+).select do |flag|
+ try_link('int main() {return 0;}', flag)
+end.each do |flag|
+ CONFIG['CXXFLAGS'] << ' ' << flag
+end
+puts "CXXFLAGS=#{CONFIG['CXXFLAGS']}"
+
+# Solaris C++ compiler doesn't have make_pair()
+add_define 'HAVE_MAKE_PAIR' if try_link(<<SRC, '-lstdc++')
+ #include <utility>
+ using namespace std;
+ int main(){ pair<const int,int> tuple = make_pair(1,2); }
+SRC
+TRY_LINK.sub!('$(CXX)', '$(CC)')
+
+create_makefile "rubyeventmachine"
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/Makefile b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/Makefile
new file mode 100644
index 0000000..b64d9b4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/Makefile
@@ -0,0 +1,273 @@
+
+SHELL = /bin/sh
+
+# V=0 quiet, V=1 verbose. other values don't work.
+V = 0
+V0 = $(V:0=)
+Q1 = $(V:1=)
+Q = $(Q1:0=@)
+ECHO1 = $(V:1=@ :)
+ECHO = $(ECHO1:0=@ echo)
+NULLCMD = :
+
+#### Start of system configuration section. ####
+
+srcdir = .
+topdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0
+hdrdir = $(topdir)
+arch_hdrdir = /Users/ben/.rubies/ruby-3.4.1/include/ruby-3.4.0/arm64-darwin24
+PATH_SEPARATOR = :
+VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
+prefix = $(DESTDIR)/Users/ben/.rubies/ruby-3.4.1
+rubysitearchprefix = $(rubylibprefix)/$(sitearch)
+rubyarchprefix = $(rubylibprefix)/$(arch)
+rubylibprefix = $(libdir)/$(RUBY_BASE_NAME)
+exec_prefix = $(prefix)
+vendorarchhdrdir = $(vendorhdrdir)/$(sitearch)
+sitearchhdrdir = $(sitehdrdir)/$(sitearch)
+rubyarchhdrdir = $(rubyhdrdir)/$(arch)
+vendorhdrdir = $(rubyhdrdir)/vendor_ruby
+sitehdrdir = $(rubyhdrdir)/site_ruby
+rubyhdrdir = $(includedir)/$(RUBY_VERSION_NAME)
+vendorarchdir = $(vendorlibdir)/$(sitearch)
+vendorlibdir = $(vendordir)/$(ruby_version)
+vendordir = $(rubylibprefix)/vendor_ruby
+sitearchdir = $(sitelibdir)/$(sitearch)
+sitelibdir = $(sitedir)/$(ruby_version)
+sitedir = $(rubylibprefix)/site_ruby
+rubyarchdir = $(rubylibdir)/$(arch)
+rubylibdir = $(rubylibprefix)/$(ruby_version)
+sitearchincludedir = $(includedir)/$(sitearch)
+archincludedir = $(includedir)/$(arch)
+sitearchlibdir = $(libdir)/$(sitearch)
+archlibdir = $(libdir)/$(arch)
+ridir = $(datarootdir)/$(RI_BASE_NAME)
+modular_gc_dir = $(DESTDIR)
+mandir = $(datarootdir)/man
+localedir = $(datarootdir)/locale
+libdir = $(exec_prefix)/lib
+psdir = $(docdir)
+pdfdir = $(docdir)
+dvidir = $(docdir)
+htmldir = $(docdir)
+infodir = $(datarootdir)/info
+docdir = $(datarootdir)/doc/$(PACKAGE)
+oldincludedir = $(DESTDIR)/usr/include
+includedir = $(SDKROOT)$(prefix)/include
+runstatedir = $(localstatedir)/run
+localstatedir = $(prefix)/var
+sharedstatedir = $(prefix)/com
+sysconfdir = $(prefix)/etc
+datadir = $(datarootdir)
+datarootdir = $(prefix)/share
+libexecdir = $(exec_prefix)/libexec
+sbindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/bin
+archdir = $(rubyarchdir)
+
+
+CC_WRAPPER =
+CC = clang
+CXX = clang++ -std=gnu++11
+LIBRUBY = $(LIBRUBY_A)
+LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
+LIBRUBYARG_SHARED =
+LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static -framework CoreFoundation $(MAINLIBS)
+empty =
+OUTFLAG = -o $(empty)
+COUTFLAG = -o $(empty)
+CSRCFLAG = $(empty)
+
+RUBY_EXTCONF_H =
+cflags = $(hardenflags) -fdeclspec $(optflags) $(debugflags) $(warnflags)
+cxxflags =
+optflags = -O3 -fno-fast-math
+debugflags = -ggdb3
+warnflags = -Wall -Wextra -Wextra-tokens -Wdeprecated-declarations -Wdivision-by-zero -Wdiv-by-zero -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wshorten-64-to-32 -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wunused-variable -Wmisleading-indentation -Wundef
+cppflags =
+CCDLFLAGS = -fno-common
+CFLAGS = $(CCDLFLAGS) $(cflags) -pipe $(ARCH_FLAG)
+INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
+DEFS =
+CPPFLAGS = -DBUILD_FOR_RUBY -DOS_UNIX -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/libyaml/include -I/opt/homebrew/opt/gdbm/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT $(DEFS) $(cppflags)
+CXXFLAGS = $(CCDLFLAGS) -fdeclspec $(ARCH_FLAG)
+ldflags = -L. -fstack-protector-strong -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib
+dldflags = -L/opt/homebrew/Cellar/readline/8.2.13/lib -L/opt/homebrew/Cellar/libyaml/0.2.5/lib -L/opt/homebrew/Cellar/gdbm/1.24/lib -Wl,-undefined,dynamic_lookup -bundle_loader '$(BUILTRUBY)'
+ARCH_FLAG = -arch arm64
+DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG)
+LDSHARED = $(CXX) -dynamic -bundle
+LDSHAREDXX = $(CXX) -dynamic -bundle
+POSTLINK = dsymutil $@ 2>/dev/null; { test -z '$(RUBY_CODESIGN)' || codesign -s '$(RUBY_CODESIGN)' $@; }
+AR = ar
+LD = ld
+EXEEXT =
+
+RUBY_INSTALL_NAME = $(RUBY_BASE_NAME)
+RUBY_SO_NAME = ruby.3.4
+RUBYW_INSTALL_NAME =
+RUBY_VERSION_NAME = $(RUBY_BASE_NAME)-$(ruby_version)
+RUBYW_BASE_NAME = rubyw
+RUBY_BASE_NAME = ruby
+
+arch = arm64-darwin24
+sitearch = $(arch)
+ruby_version = 3.4.0
+ruby = $(bindir)/$(RUBY_BASE_NAME)
+RUBY = $(ruby)
+BUILTRUBY = $(bindir)/$(RUBY_BASE_NAME)
+ruby_headers = $(hdrdir)/ruby.h $(hdrdir)/ruby/backward.h $(hdrdir)/ruby/ruby.h $(hdrdir)/ruby/defines.h $(hdrdir)/ruby/missing.h $(hdrdir)/ruby/intern.h $(hdrdir)/ruby/st.h $(hdrdir)/ruby/subst.h $(arch_hdrdir)/ruby/config.h
+
+RM = rm -f
+RM_RF = rm -fr
+RMDIRS = rmdir -p
+MAKEDIRS = mkdir -p
+INSTALL = /usr/bin/install -c
+INSTALL_PROG = $(INSTALL) -m 0755
+INSTALL_DATA = $(INSTALL) -m 644
+COPY = cp
+TOUCH = exit >
+
+#### End of system configuration section. ####
+
+preload =
+libpath = . $(libdir) /opt/homebrew/opt/readline/lib /opt/homebrew/opt/libyaml/lib /opt/homebrew/opt/gdbm/lib
+LIBPATH = -L. -L$(libdir) -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/libyaml/lib -L/opt/homebrew/opt/gdbm/lib
+DEFFILE =
+
+CLEANFILES = mkmf.log
+DISTCLEANFILES =
+DISTCLEANDIRS =
+
+extout =
+extout_prefix =
+target_prefix =
+LOCAL_LIBS =
+LIBS = -lpthread
+ORIG_SRCS = mapper.cpp rubymain.cpp
+SRCS = $(ORIG_SRCS)
+OBJS = mapper.o rubymain.o
+HDRS = $(srcdir)/mapper.h
+LOCAL_HDRS =
+TARGET = fastfilereaderext
+TARGET_NAME = fastfilereaderext
+TARGET_ENTRY = Init_$(TARGET_NAME)
+DLLIB = $(TARGET).bundle
+EXTSTATIC =
+STATIC_LIB =
+
+TIMESTAMP_DIR = .
+BINDIR = $(bindir)
+RUBYCOMMONDIR = $(sitedir)$(target_prefix)
+RUBYLIBDIR = $(sitelibdir)$(target_prefix)
+RUBYARCHDIR = $(sitearchdir)$(target_prefix)
+HDRDIR = $(sitehdrdir)$(target_prefix)
+ARCHHDRDIR = $(sitearchhdrdir)$(target_prefix)
+TARGET_SO_DIR =
+TARGET_SO = $(TARGET_SO_DIR)$(DLLIB)
+CLEANLIBS = $(TARGET_SO) $(TARGET_SO:=.dSYM)
+CLEANOBJS = $(OBJS) *.bak
+TARGET_SO_DIR_TIMESTAMP = $(TIMESTAMP_DIR)/.sitearchdir.time
+
+all: $(DLLIB)
+static: $(STATIC_LIB)
+.PHONY: all install static install-so install-rb
+.PHONY: clean clean-so clean-static clean-rb
+
+clean-static::
+clean-rb-default::
+clean-rb::
+clean-so::
+clean: clean-so clean-static clean-rb-default clean-rb
+ -$(Q)$(RM_RF) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) .*.time
+
+distclean-rb-default::
+distclean-rb::
+distclean-so::
+distclean-static::
+distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb
+ -$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
+ -$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
+ -$(Q)$(RMDIRS) $(DISTCLEANDIRS) 2> /dev/null || true
+
+realclean: distclean
+install: install-so install-rb
+
+install-so: $(DLLIB) $(TARGET_SO_DIR_TIMESTAMP)
+ $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
+clean-static::
+ -$(Q)$(RM) $(STATIC_LIB)
+install-rb: pre-install-rb do-install-rb install-rb-default
+install-rb-default: pre-install-rb-default do-install-rb-default
+pre-install-rb: Makefile
+pre-install-rb-default: Makefile
+do-install-rb:
+do-install-rb-default:
+pre-install-rb-default:
+ @$(NULLCMD)
+$(TARGET_SO_DIR_TIMESTAMP):
+ $(Q) $(MAKEDIRS) $(@D) $(RUBYARCHDIR)
+ $(Q) $(TOUCH) $@
+
+site-install: site-install-so site-install-rb
+site-install-so: install-so
+site-install-rb: install-rb
+
+.SUFFIXES: .c .m .cc .mm .cxx .cpp .o .S
+
+.cc.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cc.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.mm.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.mm.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cxx.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cxx.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.cpp.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.cpp.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.c.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.c.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+.m.o:
+ $(ECHO) compiling $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $(CSRCFLAG)$<
+
+.m.S:
+ $(ECHO) translating $(<)
+ $(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -S $(CSRCFLAG)$<
+
+$(TARGET_SO): $(OBJS) Makefile
+ $(ECHO) linking shared-object $(DLLIB)
+ -$(Q)$(RM) $(@)
+ $(Q) $(LDSHAREDXX) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
+ $(Q) $(POSTLINK)
+
+
+
+$(OBJS): $(HDRS) $(ruby_headers)
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/extconf.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/extconf.rb
new file mode 100644
index 0000000..e4d64e4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/extconf.rb
@@ -0,0 +1,109 @@
+require 'mkmf'
+
+def check_libs libs = [], fatal = false
+ libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) }
+end
+
+def check_heads heads = [], fatal = false
+ heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)}
+end
+
+def add_define(name)
+ $defs.push("-D#{name}")
+end
+
+# Eager check devs tools
+have_devel? if respond_to?(:have_devel?)
+
+add_define 'BUILD_FOR_RUBY'
+
+# Minor platform details between *nix and Windows:
+
+if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/
+ GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw'
+ OS_WIN32 = true
+ add_define "OS_WIN32"
+else
+ GNU_CHAIN = true
+ OS_UNIX = true
+ add_define 'OS_UNIX'
+end
+
+# Adjust number of file descriptors (FD) on Windows
+
+if RbConfig::CONFIG["host_os"] =~ /mingw/
+ found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS").
+ any? { |v| v.include?("FD_SETSIZE") }
+
+ add_define "FD_SETSIZE=32767" unless found
+end
+
+# Main platform invariances:
+
+case RUBY_PLATFORM
+when /mswin32/, /mingw32/, /bccwin32/
+ check_heads(%w[windows.h winsock.h], true)
+ check_libs(%w[kernel32 rpcrt4 gdi32], true)
+
+ if GNU_CHAIN
+ CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++"
+ else
+ $defs.push "-EHs"
+ $defs.push "-GR"
+ end
+
+when /solaris/
+ add_define 'OS_SOLARIS8'
+ check_libs(%w[nsl socket], true)
+
+ if CONFIG['CC'] == 'cc' && (
+ `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler
+ `cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler
+ )
+ # SUN CHAIN
+ add_define 'CC_SUNWspro'
+ $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile
+ $CFLAGS = CONFIG['CFLAGS'] = "-KPIC"
+ CONFIG['CCDLFLAGS'] = "-KPIC"
+ CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd"
+ CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd"
+ else
+ # GNU CHAIN
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+ end
+
+when /openbsd/
+ # OpenBSD branch contributed by Guillaume Sellier.
+
+ # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs
+ CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC"
+ CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC"
+
+when /darwin/
+ # on Unix we need a g++ link, not gcc.
+ # Ff line contributed by Daniel Harple.
+ CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
+
+when /linux/
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+when /aix/
+ CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl"
+
+when /cygwin/
+ # For rubies built with Cygwin, CXX may be set to CC, which is just
+ # a wrapper for gcc.
+ # This will compile, but it will not link to the C++ std library.
+ # Explicitly set CXX to use g++.
+ CONFIG['CXX'] = "g++"
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+
+else
+ # on Unix we need a g++ link, not gcc.
+ CONFIG['LDSHARED'] = "$(CXX) -shared"
+end
+
+create_makefile "fastfilereaderext"
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.cpp
new file mode 100644
index 0000000..f226f2d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.cpp
@@ -0,0 +1,214 @@
+/*****************************************************************************
+
+$Id: mapper.cpp 4527 2007-07-04 10:21:34Z francis $
+
+File: mapper.cpp
+Date: 02Jul07
+
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
+Gmail: garbagecat10
+
+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.
+
+*****************************************************************************/
+
+
+//////////////////////////////////////////////////////////////////////
+// UNIX implementation
+//////////////////////////////////////////////////////////////////////
+
+
+#ifdef OS_UNIX
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <cstring>
+#include <stdexcept>
+
+#include "mapper.h"
+
+/******************
+Mapper_t::Mapper_t
+******************/
+
+Mapper_t::Mapper_t (const std::string &filename)
+{
+ /* We ASSUME we can open the file.
+ * (More precisely, we assume someone else checked before we got here.)
+ */
+
+ Fd = open (filename.c_str(), O_RDONLY);
+ if (Fd < 0)
+ throw std::runtime_error (strerror (errno));
+
+ struct stat st;
+ if (fstat (Fd, &st))
+ throw std::runtime_error (strerror (errno));
+ FileSize = st.st_size;
+
+ #ifdef OS_WIN32
+ MapPoint = (char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
+ #else
+ MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0);
+ #endif
+ if (MapPoint == MAP_FAILED)
+ throw std::runtime_error (strerror (errno));
+}
+
+
+/*******************
+Mapper_t::~Mapper_t
+*******************/
+
+Mapper_t::~Mapper_t()
+{
+ Close();
+}
+
+
+/***************
+Mapper_t::Close
+***************/
+
+void Mapper_t::Close()
+{
+ // Can be called multiple times.
+ // Calls to GetChunk are invalid after a call to Close.
+ if (MapPoint) {
+ #ifdef CC_SUNWspro
+ // TODO: The void * cast works fine on Solaris 11, but
+ // I don't know at what point that changed from older Solaris.
+ munmap ((char*)MapPoint, FileSize);
+ #else
+ munmap ((void*)MapPoint, FileSize);
+ #endif
+ MapPoint = NULL;
+ }
+ if (Fd >= 0) {
+ close (Fd);
+ Fd = -1;
+ }
+}
+
+/******************
+Mapper_t::GetChunk
+******************/
+
+const char *Mapper_t::GetChunk (unsigned start)
+{
+ return MapPoint + start;
+}
+
+
+
+#endif // OS_UNIX
+
+
+//////////////////////////////////////////////////////////////////////
+// WINDOWS implementation
+//////////////////////////////////////////////////////////////////////
+
+#ifdef OS_WIN32
+
+#include <windows.h>
+
+#include <iostream>
+#include <string>
+#include <stdexcept>
+
+#include "mapper.h"
+
+/******************
+Mapper_t::Mapper_t
+******************/
+
+Mapper_t::Mapper_t (const std::string &filename)
+{
+ /* We ASSUME we can open the file.
+ * (More precisely, we assume someone else checked before we got here.)
+ */
+
+ hFile = INVALID_HANDLE_VALUE;
+ hMapping = NULL;
+ MapPoint = NULL;
+ FileSize = 0;
+
+ hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ throw std::runtime_error ("File not found");
+
+ BY_HANDLE_FILE_INFORMATION i;
+ if (GetFileInformationByHandle (hFile, &i))
+ FileSize = i.nFileSizeLow;
+
+ hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
+ if (!hMapping)
+ throw std::runtime_error ("File not mapped");
+
+ #ifdef OS_WIN32
+ MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
+ #else
+ MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0);
+ #endif
+ if (!MapPoint)
+ throw std::runtime_error ("Mappoint not read");
+}
+
+
+/*******************
+Mapper_t::~Mapper_t
+*******************/
+
+Mapper_t::~Mapper_t()
+{
+ Close();
+}
+
+/***************
+Mapper_t::Close
+***************/
+
+void Mapper_t::Close()
+{
+ // Can be called multiple times.
+ // Calls to GetChunk are invalid after a call to Close.
+ if (MapPoint) {
+ UnmapViewOfFile (MapPoint);
+ MapPoint = NULL;
+ }
+ if (hMapping != NULL) {
+ CloseHandle (hMapping);
+ hMapping = NULL;
+ }
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle (hFile);
+ hFile = INVALID_HANDLE_VALUE;
+ }
+}
+
+
+/******************
+Mapper_t::GetChunk
+******************/
+
+const char *Mapper_t::GetChunk (unsigned start)
+{
+ return MapPoint + start;
+}
+
+
+
+#endif // OS_WINDOWS
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.h
new file mode 100644
index 0000000..3db0eea
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/mapper.h
@@ -0,0 +1,59 @@
+/*****************************************************************************
+
+$Id: mapper.h 4529 2007-07-04 11:32:22Z francis $
+
+File: mapper.h
+Date: 02Jul07
+
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
+Gmail: garbagecat10
+
+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.
+
+*****************************************************************************/
+
+
+#ifndef __Mapper__H_
+#define __Mapper__H_
+
+
+/**************
+class Mapper_t
+**************/
+
+class Mapper_t
+{
+ public:
+ Mapper_t (const std::string&);
+ virtual ~Mapper_t();
+
+ const char *GetChunk (unsigned);
+ void Close();
+ size_t GetFileSize() {return FileSize;}
+
+ private:
+ size_t FileSize;
+
+ #ifdef OS_UNIX
+ private:
+ int Fd;
+ const char *MapPoint;
+ #endif // OS_UNIX
+
+ #ifdef OS_WIN32
+ private:
+ HANDLE hFile;
+ HANDLE hMapping;
+ char *MapPoint;
+ #endif // OS_WIN32
+
+};
+
+
+#endif // __Mapper__H_
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/rubymain.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/rubymain.cpp
new file mode 100644
index 0000000..b962bf0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/fastfilereader/rubymain.cpp
@@ -0,0 +1,126 @@
+/*****************************************************************************
+
+$Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $
+
+File: rubymain.cpp
+Date: 02Jul07
+
+Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved.
+Gmail: garbagecat10
+
+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.
+
+*****************************************************************************/
+
+
+
+#include <iostream>
+#include <stdexcept>
+
+#include <ruby.h>
+#include "mapper.h"
+
+static VALUE EmModule;
+static VALUE FastFileReader;
+static VALUE Mapper;
+
+
+
+/*********
+mapper_dt
+*********/
+
+static void mapper_dt (void *ptr)
+{
+ if (ptr)
+ delete (Mapper_t*) ptr;
+}
+
+/**********
+mapper_new
+**********/
+
+static VALUE mapper_new (VALUE self, VALUE filename)
+{
+ Mapper_t *m = new Mapper_t (StringValueCStr (filename));
+ if (!m)
+ rb_raise (rb_eStandardError, "No Mapper Object");
+ VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m);
+ return v;
+}
+
+
+/****************
+mapper_get_chunk
+****************/
+
+static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length)
+{
+ Mapper_t *m = NULL;
+ Data_Get_Struct (self, Mapper_t, m);
+ if (!m)
+ rb_raise (rb_eStandardError, "No Mapper Object");
+
+ // TODO, what if some moron sends us a negative start value?
+ unsigned _start = NUM2INT (start);
+ unsigned _length = NUM2INT (length);
+ if ((_start + _length) > m->GetFileSize())
+ rb_raise (rb_eStandardError, "Mapper Range Error");
+
+ const char *chunk = m->GetChunk (_start);
+ if (!chunk)
+ rb_raise (rb_eStandardError, "No Mapper Chunk");
+ return rb_str_new (chunk, _length);
+}
+
+/************
+mapper_close
+************/
+
+static VALUE mapper_close (VALUE self)
+{
+ Mapper_t *m = NULL;
+ Data_Get_Struct (self, Mapper_t, m);
+ if (!m)
+ rb_raise (rb_eStandardError, "No Mapper Object");
+ m->Close();
+ return Qnil;
+}
+
+/***********
+mapper_size
+***********/
+
+static VALUE mapper_size (VALUE self)
+{
+ Mapper_t *m = NULL;
+ Data_Get_Struct (self, Mapper_t, m);
+ if (!m)
+ rb_raise (rb_eStandardError, "No Mapper Object");
+ return INT2NUM (m->GetFileSize());
+}
+
+
+/**********************
+Init_fastfilereaderext
+**********************/
+
+extern "C" void Init_fastfilereaderext()
+{
+ EmModule = rb_define_module ("EventMachine");
+ FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject);
+ Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject);
+
+ rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1);
+ rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0);
+ rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0);
+ rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2);
+}
+
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/kb.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/kb.cpp
new file mode 100644
index 0000000..2187ae2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/kb.cpp
@@ -0,0 +1,79 @@
+/*****************************************************************************
+
+$Id$
+
+File: kb.cpp
+Date: 24Aug07
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+/**************************************
+KeyboardDescriptor::KeyboardDescriptor
+**************************************/
+
+KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em):
+ EventableDescriptor (0, parent_em),
+ bReadAttemptedAfterClose (false)
+{
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+ #ifdef HAVE_KQUEUE
+ MyEventMachine->ArmKqueueReader (this);
+ #endif
+}
+
+
+/***************************************
+KeyboardDescriptor::~KeyboardDescriptor
+***************************************/
+
+KeyboardDescriptor::~KeyboardDescriptor()
+{
+}
+
+
+/*************************
+KeyboardDescriptor::Write
+*************************/
+
+void KeyboardDescriptor::Write()
+{
+ // Why are we here?
+ throw std::runtime_error ("bad code path in keyboard handler");
+}
+
+
+/*****************************
+KeyboardDescriptor::Heartbeat
+*****************************/
+
+void KeyboardDescriptor::Heartbeat()
+{
+ // no-op
+}
+
+
+/************************
+KeyboardDescriptor::Read
+************************/
+
+void KeyboardDescriptor::Read()
+{
+ char c;
+ (void)read (GetSocket(), &c, 1);
+ _GenericInboundDispatch(&c, 1);
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.cpp
new file mode 100644
index 0000000..a3a2340
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.cpp
@@ -0,0 +1,107 @@
+/*****************************************************************************
+
+$Id$
+
+File: page.cpp
+Date: 30Apr06
+
+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.
+
+*****************************************************************************/
+
+
+#include "project.h"
+
+
+/******************
+PageList::PageList
+******************/
+
+PageList::PageList()
+{
+}
+
+
+/*******************
+PageList::~PageList
+*******************/
+
+PageList::~PageList()
+{
+ while (HasPages())
+ PopFront();
+}
+
+
+/***************
+PageList::Front
+***************/
+
+void PageList::Front (const char **page, int *length)
+{
+ assert (page && length);
+
+ if (HasPages()) {
+ Page p = Pages.front();
+ *page = p.Buffer;
+ *length = p.Size;
+ }
+ else {
+ *page = NULL;
+ *length = 0;
+ }
+}
+
+
+/******************
+PageList::PopFront
+******************/
+
+void PageList::PopFront()
+{
+ if (HasPages()) {
+ Page p = Pages.front();
+ Pages.pop_front();
+ if (p.Buffer)
+ free ((void*)p.Buffer);
+ }
+}
+
+
+/******************
+PageList::HasPages
+******************/
+
+bool PageList::HasPages()
+{
+ return (Pages.size() > 0) ? true : false;
+}
+
+
+/**************
+PageList::Push
+**************/
+
+void PageList::Push (const char *buf, int size)
+{
+ if (buf && (size > 0)) {
+ char *copy = (char*) malloc (size);
+ if (!copy)
+ throw std::runtime_error ("no memory in pagelist");
+ memcpy (copy, buf, size);
+ Pages.push_back (Page (copy, size));
+ }
+}
+
+
+
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.h
new file mode 100644
index 0000000..969fc91
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/page.h
@@ -0,0 +1,51 @@
+/*****************************************************************************
+
+$Id$
+
+File: page.h
+Date: 30Apr06
+
+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.
+
+*****************************************************************************/
+
+
+#ifndef __PageManager__H_
+#define __PageManager__H_
+
+
+/**************
+class PageList
+**************/
+
+class PageList
+{
+ struct Page {
+ Page (const char *b, size_t s): Buffer(b), Size(s) {}
+ const char *Buffer;
+ size_t Size;
+ };
+
+ public:
+ PageList();
+ virtual ~PageList();
+
+ void Push (const char*, int);
+ bool HasPages();
+ void Front (const char**, int*);
+ void PopFront();
+
+ private:
+ std::deque<Page> Pages;
+};
+
+
+#endif // __PageManager__H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/pipe.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/pipe.cpp
new file mode 100644
index 0000000..a5f0d9f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/pipe.cpp
@@ -0,0 +1,354 @@
+/*****************************************************************************
+
+$Id$
+
+File: pipe.cpp
+Date: 30May07
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+
+
+#ifdef OS_UNIX
+// THIS ENTIRE FILE IS ONLY COMPILED ON UNIX-LIKE SYSTEMS.
+
+/******************************
+PipeDescriptor::PipeDescriptor
+******************************/
+
+PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em):
+ EventableDescriptor (fd, parent_em),
+ bReadAttemptedAfterClose (false),
+ OutboundDataSize (0),
+ SubprocessPid (subpid)
+{
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ #endif
+ #ifdef HAVE_KQUEUE
+ MyEventMachine->ArmKqueueReader (this);
+ #endif
+}
+
+
+/*******************************
+PipeDescriptor::~PipeDescriptor
+*******************************/
+
+PipeDescriptor::~PipeDescriptor() NO_EXCEPT_FALSE
+{
+ // Run down any stranded outbound data.
+ for (size_t i=0; i < OutboundPages.size(); i++)
+ OutboundPages[i].Free();
+
+ /* As a virtual destructor, we come here before the base-class
+ * destructor that closes our file-descriptor.
+ * We have to make sure the subprocess goes down (if it's not
+ * already down) and we have to reap the zombie.
+ *
+ * This implementation is PROVISIONAL and will surely be improved.
+ * The intention here is that we never block, hence the highly
+ * undesirable sleeps. But if we can't reap the subprocess even
+ * after sending it SIGKILL, then something is wrong and we
+ * throw a fatal exception, which is also not something we should
+ * be doing.
+ *
+ * Eventually the right thing to do will be to have the reactor
+ * core respond to SIGCHLD by chaining a handler on top of the
+ * one Ruby may have installed, and dealing with a list of dead
+ * children that are pending cleanup.
+ *
+ * Since we want to have a signal processor integrated into the
+ * client-visible API, let's wait until that is done before cleaning
+ * this up.
+ *
+ * Added a very ugly hack to support passing the subprocess's exit
+ * status to the user. It only makes logical sense for user code to access
+ * the subprocess exit status in the unbind callback. But unbind is called
+ * back during the EventableDescriptor destructor. So by that time there's
+ * no way to call back this object through an object binding, because it's
+ * already been cleaned up. We might have added a parameter to the unbind
+ * callback, but that would probably break a huge amount of existing code.
+ * So the hack-solution is to define an instance variable in the EventMachine
+ * object and stick the exit status in there, where it can easily be accessed
+ * with an accessor visible to user code.
+ * User code should ONLY access the exit status from within the unbind callback.
+ * Otherwise there's no guarantee it'll be valid.
+ * This hack won't make it impossible to run multiple EventMachines in a single
+ * process, but it will make it impossible to reliably nest unbind calls
+ * within other unbind calls. (Not sure if that's even possible.)
+ */
+
+ assert (MyEventMachine);
+
+ /* Another hack to make the SubprocessPid available to get_subprocess_status */
+ MyEventMachine->SubprocessPid = SubprocessPid;
+
+ /* 01Mar09: Updated to use a small nanosleep in a loop. When nanosleep is interrupted by SIGCHLD,
+ * it resumes the system call after processing the signal (resulting in unnecessary latency).
+ * Calling nanosleep in a loop avoids this problem.
+ */
+ struct timespec req = {0, 50000000}; // 0.05s
+ int n;
+
+ // wait 0.5s for the process to die
+ for (n=0; n<10; n++) {
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+ nanosleep (&req, NULL);
+ }
+
+ // send SIGTERM and wait another 1s
+ kill (SubprocessPid, SIGTERM);
+ for (n=0; n<20; n++) {
+ nanosleep (&req, NULL);
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+ }
+
+ // send SIGKILL and wait another 5s
+ kill (SubprocessPid, SIGKILL);
+ for (n=0; n<100; n++) {
+ nanosleep (&req, NULL);
+ if (waitpid (SubprocessPid, &(MyEventMachine->SubprocessExitStatus), WNOHANG) != 0) return;
+ }
+
+ // still not dead, give up!
+ throw std::runtime_error ("unable to reap subprocess");
+}
+
+
+
+/********************
+PipeDescriptor::Read
+********************/
+
+void PipeDescriptor::Read()
+{
+ int sd = GetSocket();
+ if (sd == INVALID_SOCKET) {
+ assert (!bReadAttemptedAfterClose);
+ bReadAttemptedAfterClose = true;
+ return;
+ }
+
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+
+ int total_bytes_read = 0;
+ char readbuffer [16 * 1024];
+
+ for (int i=0; i < 10; i++) {
+ // Don't read just one buffer and then move on. This is faster
+ // if there is a lot of incoming.
+ // But don't read indefinitely. Give other sockets a chance to run.
+ // NOTICE, we're reading one less than the buffer size.
+ // That's so we can put a guard byte at the end of what we send
+ // to user code.
+ // Use read instead of recv, which on Linux gives a "socket operation
+ // on nonsocket" error.
+
+
+ int r = read (sd, readbuffer, sizeof(readbuffer) - 1);
+ //cerr << "<R:" << r << ">";
+
+ if (r > 0) {
+ total_bytes_read += r;
+
+ // Add a null-terminator at the the end of the buffer
+ // that we will send to the callback.
+ // DO NOT EVER CHANGE THIS. We want to explicitly allow users
+ // to be able to depend on this behavior, so they will have
+ // the option to do some things faster. Additionally it's
+ // a security guard against buffer overflows.
+ readbuffer [r] = 0;
+ _GenericInboundDispatch(readbuffer, r);
+ }
+ else if (r == 0) {
+ break;
+ }
+ else {
+ // Basically a would-block, meaning we've read everything there is to read.
+ break;
+ }
+
+ }
+
+
+ if (total_bytes_read == 0) {
+ // If we read no data on a socket that selected readable,
+ // it generally means the other end closed the connection gracefully.
+ ScheduleClose (false);
+ //bCloseNow = true;
+ }
+
+}
+
+/*********************
+PipeDescriptor::Write
+*********************/
+
+void PipeDescriptor::Write()
+{
+ int sd = GetSocket();
+ assert (sd != INVALID_SOCKET);
+
+ LastActivity = MyEventMachine->GetCurrentLoopTime();
+ char output_buffer [16 * 1024];
+ size_t nbytes = 0;
+
+ while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) {
+ OutboundPage *op = &(OutboundPages[0]);
+ if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) {
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset);
+ nbytes += (op->Length - op->Offset);
+ op->Free();
+ OutboundPages.pop_front();
+ }
+ else {
+ int len = sizeof(output_buffer) - nbytes;
+ memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len);
+ op->Offset += len;
+ nbytes += len;
+ }
+ }
+
+ // We should never have gotten here if there were no data to write,
+ // so assert that as a sanity check.
+ // Don't bother to make sure nbytes is less than output_buffer because
+ // if it were we probably would have crashed already.
+ assert (nbytes > 0);
+
+ assert (GetSocket() != INVALID_SOCKET);
+ int bytes_written = write (GetSocket(), output_buffer, nbytes);
+#ifdef OS_WIN32
+ int e = WSAGetLastError();
+#else
+ int e = errno;
+#endif
+
+ if (bytes_written > 0) {
+ OutboundDataSize -= bytes_written;
+ if ((size_t)bytes_written < nbytes) {
+ int len = nbytes - bytes_written;
+ char *buffer = (char*) malloc (len + 1);
+ if (!buffer)
+ throw std::runtime_error ("bad alloc throwing back data");
+ memcpy (buffer, output_buffer + bytes_written, len);
+ buffer [len] = 0;
+ OutboundPages.push_front (OutboundPage (buffer, len));
+ }
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = EPOLLIN;
+ if (SelectForWrite())
+ EpollEvent.events |= EPOLLOUT;
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+ }
+ else {
+ #ifdef OS_UNIX
+ if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR))
+ #endif
+ #ifdef OS_WIN32
+ if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK))
+ #endif
+ Close();
+ }
+}
+
+
+/*************************
+PipeDescriptor::Heartbeat
+*************************/
+
+void PipeDescriptor::Heartbeat()
+{
+ // If an inactivity timeout is defined, then check for it.
+ if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout))
+ ScheduleClose (false);
+ //bCloseNow = true;
+}
+
+
+/*****************************
+PipeDescriptor::SelectForRead
+*****************************/
+
+bool PipeDescriptor::SelectForRead()
+{
+ /* Pipe descriptors, being local by definition, don't have
+ * a pending state, so this is simpler than for the
+ * ConnectionDescriptor object.
+ */
+ return bPaused ? false : true;
+}
+
+/******************************
+PipeDescriptor::SelectForWrite
+******************************/
+
+bool PipeDescriptor::SelectForWrite()
+{
+ /* Pipe descriptors, being local by definition, don't have
+ * a pending state, so this is simpler than for the
+ * ConnectionDescriptor object.
+ */
+ return (GetOutboundDataSize() > 0) && !bPaused ? true : false;
+}
+
+
+
+
+/********************************
+PipeDescriptor::SendOutboundData
+********************************/
+
+int PipeDescriptor::SendOutboundData (const char *data, unsigned long length)
+{
+ //if (bCloseNow || bCloseAfterWriting)
+ if (IsCloseScheduled())
+ return 0;
+
+ if (!data && (length > 0))
+ throw std::runtime_error ("bad outbound data");
+ char *buffer = (char *) malloc (length + 1);
+ if (!buffer)
+ throw std::runtime_error ("no allocation for outbound data");
+ memcpy (buffer, data, length);
+ buffer [length] = 0;
+ OutboundPages.push_back (OutboundPage (buffer, length));
+ OutboundDataSize += length;
+ #ifdef HAVE_EPOLL
+ EpollEvent.events = (EPOLLIN | EPOLLOUT);
+ assert (MyEventMachine);
+ MyEventMachine->Modify (this);
+ #endif
+ return length;
+}
+
+/********************************
+PipeDescriptor::GetSubprocessPid
+********************************/
+
+bool PipeDescriptor::GetSubprocessPid (pid_t *pid)
+{
+ bool ok = false;
+ if (pid && (SubprocessPid > 0)) {
+ *pid = SubprocessPid;
+ ok = true;
+ }
+ return ok;
+}
+
+
+#endif // OS_UNIX
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/project.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/project.h
new file mode 100644
index 0000000..757f701
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/project.h
@@ -0,0 +1,174 @@
+/*****************************************************************************
+
+$Id$
+
+File: project.h
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+
+#ifndef __Project__H_
+#define __Project__H_
+
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <vector>
+#include <deque>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+
+#ifdef OS_UNIX
+#include <signal.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <pwd.h>
+#include <string.h>
+typedef int SOCKET;
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#ifdef OS_SOLARIS8
+#include <strings.h>
+#include <sys/un.h>
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+// INADDR_NONE is undefined on Solaris < 8. Thanks to Brett Eisenberg and Tim Pease.
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long)-1)
+#endif
+#endif /* OS_SOLARIS8 */
+
+#ifdef _AIX
+#include <strings.h>
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+#endif /* _AIX */
+
+#ifdef OS_DARWIN
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* OS_DARWIN */
+
+#endif /* OS_UNIX */
+
+#ifdef OS_WIN32
+// 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h)
+// 18Jun12: fd_setsize must be changed in the ruby binary (not in this extension). redefining it also causes segvs, see eventmachine/eventmachine#333
+//#define FD_SETSIZE 1024
+
+// WIN32_LEAN_AND_MEAN excludes APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets.
+#define WIN32_LEAN_AND_MEAN
+
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <rpc.h>
+#include <fcntl.h>
+#include <assert.h>
+
+// Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0x0400
+#endif
+
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0x0008
+#endif
+
+// Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function
+#define RUBY_EXPORT
+#include <ruby/defines.h>
+#include <ruby/win32.h>
+#endif /* OS_WIN32 */
+
+#if !defined(_MSC_VER) || _MSC_VER > 1500
+#include <stdint.h>
+#endif
+
+#ifdef WITH_SSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#endif
+
+#ifdef HAVE_KQUEUE
+#include <sys/event.h>
+#include <sys/queue.h>
+#endif
+
+#ifdef HAVE_INOTIFY
+#include <sys/inotify.h>
+#endif
+
+#ifdef HAVE_OLD_INOTIFY
+#include <sys/syscall.h>
+#include <linux/inotify.h>
+static inline int inotify_init (void) { return syscall (__NR_inotify_init); }
+static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); }
+static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); }
+#define HAVE_INOTIFY 1
+#endif
+
+#ifdef HAVE_INOTIFY
+#define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event))
+#endif
+
+#ifdef HAVE_WRITEV
+#include <sys/uio.h>
+#endif
+
+#if __cplusplus
+extern "C" {
+#endif
+ typedef void (*EMCallback)(const unsigned long, int, const char*, const unsigned long);
+#if __cplusplus
+}
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif
+
+#include "binder.h"
+#include "em.h"
+#include "ed.h"
+#include "page.h"
+#include "ssl.h"
+#include "eventmachine.h"
+
+#endif // __Project__H_
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/rubymain.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/rubymain.cpp
new file mode 100644
index 0000000..305f9cb
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/rubymain.cpp
@@ -0,0 +1,1515 @@
+/*****************************************************************************
+
+$Id$
+
+File: rubymain.cpp
+Date: 06Apr06
+
+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.
+
+*****************************************************************************/
+
+#include "project.h"
+#include "eventmachine.h"
+#include <ruby.h>
+
+#ifndef RFLOAT_VALUE
+#define RFLOAT_VALUE(arg) RFLOAT(arg)->value
+#endif
+
+/* Adapted from NUM2BSIG / BSIG2NUM in ext/fiddle/conversions.h,
+ * we'll call it a BSIG for Binding Signature here. */
+#if SIZEOF_VOIDP == SIZEOF_LONG
+# define BSIG2NUM(x) (ULONG2NUM((unsigned long)(x)))
+# define NUM2BSIG(x) (NUM2ULONG(x))
+# ifdef OS_WIN32
+# define PRIFBSIG "I32u"
+# else
+# define PRIFBSIG "lu"
+# endif
+#else
+# define BSIG2NUM(x) (ULL2NUM((unsigned long long)(x)))
+# define NUM2BSIG(x) (NUM2ULL(x))
+# ifdef OS_WIN32
+# define PRIFBSIG "I64u"
+# else
+# define PRIFBSIG "llu"
+# endif
+#endif
+
+/*******
+Statics
+*******/
+
+static VALUE EmModule;
+static VALUE EmConnection;
+static VALUE EmConnsHash;
+static VALUE EmTimersHash;
+
+static VALUE EM_eConnectionError;
+static VALUE EM_eUnknownTimerFired;
+static VALUE EM_eConnectionNotBound;
+static VALUE EM_eUnsupported;
+static VALUE EM_eInvalidSignature;
+
+static VALUE Intern_at_signature;
+static VALUE Intern_at_timers;
+static VALUE Intern_at_conns;
+static VALUE Intern_at_error_handler;
+static VALUE Intern_event_callback;
+static VALUE Intern_run_deferred_callbacks;
+static VALUE Intern_delete;
+static VALUE Intern_call;
+static VALUE Intern_at;
+static VALUE Intern_receive_data;
+static VALUE Intern_ssl_handshake_completed;
+static VALUE Intern_ssl_verify_peer;
+static VALUE Intern_notify_readable;
+static VALUE Intern_notify_writable;
+static VALUE Intern_proxy_target_unbound;
+static VALUE Intern_proxy_completed;
+static VALUE Intern_connection_completed;
+
+static VALUE rb_cProcStatus;
+
+struct em_event {
+ uintptr_t signature;
+ int event;
+ const char *data_str;
+ unsigned long data_num;
+};
+
+static inline VALUE ensure_conn(const uintptr_t signature)
+{
+ VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature));
+ if (conn == Qnil)
+ rb_raise (EM_eConnectionNotBound, "unknown connection: %" PRIFBSIG, signature);
+ return conn;
+}
+
+
+/****************
+t_event_callback
+****************/
+
+static inline void event_callback (struct em_event* e)
+{
+ const uintptr_t signature = e->signature;
+ int event = e->event;
+ const char *data_str = e->data_str;
+ const unsigned long data_num = e->data_num;
+
+ switch (event) {
+ case EM_CONNECTION_READ:
+ {
+ VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature));
+ if (conn == Qnil)
+ rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %" PRIFBSIG, data_num, signature);
+ rb_funcall (conn, Intern_receive_data, 1, rb_str_new (data_str, data_num));
+ return;
+ }
+ case EM_CONNECTION_ACCEPTED:
+ {
+ rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
+ return;
+ }
+ case EM_CONNECTION_UNBOUND:
+ {
+ rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num));
+ return;
+ }
+ case EM_CONNECTION_COMPLETED:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_connection_completed, 0);
+ return;
+ }
+ case EM_CONNECTION_NOTIFY_READABLE:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_notify_readable, 0);
+ return;
+ }
+ case EM_CONNECTION_NOTIFY_WRITABLE:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_notify_writable, 0);
+ return;
+ }
+ case EM_LOOPBREAK_SIGNAL:
+ {
+ rb_funcall (EmModule, Intern_run_deferred_callbacks, 0);
+ return;
+ }
+ case EM_TIMER_FIRED:
+ {
+ VALUE timer = rb_funcall (EmTimersHash, Intern_delete, 1, ULONG2NUM (data_num));
+ if (timer == Qnil) {
+ rb_raise (EM_eUnknownTimerFired, "no such timer: %lu", data_num);
+ } else if (timer == Qfalse) {
+ /* Timer Canceled */
+ } else {
+ rb_funcall (timer, Intern_call, 0);
+ }
+ return;
+ }
+ #ifdef WITH_SSL
+ case EM_SSL_HANDSHAKE_COMPLETED:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_ssl_handshake_completed, 0);
+ return;
+ }
+ case EM_SSL_VERIFY:
+ {
+ VALUE conn = ensure_conn(signature);
+ VALUE should_accept = rb_funcall (conn, Intern_ssl_verify_peer, 1, rb_str_new(data_str, data_num));
+ if (RTEST(should_accept))
+ evma_accept_ssl_peer (signature);
+ return;
+ }
+ #endif
+ case EM_PROXY_TARGET_UNBOUND:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_proxy_target_unbound, 0);
+ return;
+ }
+ case EM_PROXY_COMPLETED:
+ {
+ VALUE conn = ensure_conn(signature);
+ rb_funcall (conn, Intern_proxy_completed, 0);
+ return;
+ }
+ }
+}
+
+/*******************
+event_error_handler
+*******************/
+
+static void event_error_handler(VALUE self UNUSED, VALUE err)
+{
+ VALUE error_handler = rb_ivar_get(EmModule, Intern_at_error_handler);
+ rb_funcall (error_handler, Intern_call, 1, err);
+}
+
+/**********************
+event_callback_wrapper
+**********************/
+
+static void event_callback_wrapper (const uintptr_t signature, int event, const char *data_str, const unsigned long data_num)
+{
+ struct em_event e;
+ e.signature = signature;
+ e.event = event;
+ e.data_str = data_str;
+ e.data_num = data_num;
+
+ if (!rb_ivar_defined(EmModule, Intern_at_error_handler))
+ event_callback(&e);
+ else
+ rb_rescue((VALUE (*)(ANYARGS))event_callback, (VALUE)&e, (VALUE (*)(ANYARGS))event_error_handler, Qnil);
+}
+
+/**************************
+t_initialize_event_machine
+**************************/
+
+static VALUE t_initialize_event_machine (VALUE self UNUSED)
+{
+ EmConnsHash = rb_ivar_get (EmModule, Intern_at_conns);
+ EmTimersHash = rb_ivar_get (EmModule, Intern_at_timers);
+ assert(EmConnsHash != Qnil);
+ assert(EmTimersHash != Qnil);
+ evma_initialize_library ((EMCallback)event_callback_wrapper);
+ return Qnil;
+}
+
+
+/******************
+t_run_machine_once
+******************/
+
+static VALUE t_run_machine_once (VALUE self UNUSED)
+{
+ return evma_run_machine_once () ? Qtrue : Qfalse;
+}
+
+
+/*************
+t_run_machine
+*************/
+
+static VALUE t_run_machine (VALUE self UNUSED)
+{
+ evma_run_machine();
+ return Qnil;
+}
+
+
+/*******************
+t_add_oneshot_timer
+*******************/
+
+static VALUE t_add_oneshot_timer (VALUE self UNUSED, VALUE interval)
+{
+ const uintptr_t f = evma_install_oneshot_timer (FIX2LONG (interval));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "ran out of timers; use #set_max_timers to increase limit");
+ return BSIG2NUM (f);
+}
+
+
+/**************
+t_start_server
+**************/
+
+static VALUE t_start_server (VALUE self UNUSED, VALUE server, VALUE port)
+{
+ const uintptr_t f = evma_create_tcp_server (StringValueCStr(server), FIX2INT(port));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no acceptor (port is in use or requires root privileges)");
+ return BSIG2NUM (f);
+}
+
+/*************
+t_stop_server
+*************/
+
+static VALUE t_stop_server (VALUE self UNUSED, VALUE signature)
+{
+ evma_stop_tcp_server (NUM2BSIG (signature));
+ return Qnil;
+}
+
+
+/*******************
+t_start_unix_server
+*******************/
+
+static VALUE t_start_unix_server (VALUE self UNUSED, VALUE filename)
+{
+ const uintptr_t f = evma_create_unix_domain_server (StringValueCStr(filename));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no unix-domain acceptor");
+ return BSIG2NUM (f);
+}
+
+/********************
+t_attach_sd
+********************/
+
+static VALUE t_attach_sd(VALUE self UNUSED, VALUE sd)
+{
+ const uintptr_t f = evma_attach_sd(FIX2INT(sd));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no socket descriptor acceptor");
+ return BSIG2NUM (f);
+}
+
+
+/***********
+t_send_data
+***********/
+
+static VALUE t_send_data (VALUE self UNUSED, VALUE signature, VALUE data, VALUE data_length)
+{
+ int b = evma_send_data_to_connection (NUM2BSIG (signature), StringValuePtr (data), FIX2INT (data_length));
+ return INT2NUM (b);
+}
+
+
+/***********
+t_start_tls
+***********/
+
+static VALUE t_start_tls (VALUE self UNUSED, VALUE signature)
+{
+ evma_start_tls (NUM2BSIG (signature));
+ return Qnil;
+}
+
+/***************
+t_set_tls_parms
+***************/
+
+static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE ssl_version)
+{
+ /* set_tls_parms 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 NULL strings.
+ */
+ evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (ssl_version));
+ return Qnil;
+}
+
+/***************
+t_get_peer_cert
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature)
+{
+ VALUE ret = Qnil;
+
+ X509 *cert = NULL;
+ BUF_MEM *buf;
+ BIO *out;
+
+ cert = evma_get_peer_cert (NUM2BSIG (signature));
+
+ if (cert != NULL) {
+ out = BIO_new(BIO_s_mem());
+ PEM_write_bio_X509(out, cert);
+ BIO_get_mem_ptr(out, &buf);
+ ret = rb_str_new(buf->data, buf->length);
+ X509_free(cert);
+ BIO_free(out);
+ }
+
+ return ret;
+}
+#else
+static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature UNUSED)
+{
+ return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_bits
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature)
+{
+ int bits = evma_get_cipher_bits (NUM2BSIG (signature));
+ if (bits == -1)
+ return Qnil;
+ return INT2NUM (bits);
+}
+#else
+static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED)
+{
+ return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_name
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature)
+{
+ const char *protocol = evma_get_cipher_name (NUM2BSIG (signature));
+ if (protocol)
+ return rb_str_new2 (protocol);
+
+ return Qnil;
+}
+#else
+static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature UNUSED)
+{
+ return Qnil;
+}
+#endif
+
+/***************
+t_get_cipher_protocol
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature)
+{
+ const char *cipher = evma_get_cipher_protocol (NUM2BSIG (signature));
+ if (cipher)
+ return rb_str_new2 (cipher);
+
+ return Qnil;
+}
+#else
+static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature UNUSED)
+{
+ return Qnil;
+}
+#endif
+
+/***************
+t_get_sni_hostname
+***************/
+
+#ifdef WITH_SSL
+static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature)
+{
+ const char *sni_hostname = evma_get_sni_hostname (NUM2BSIG (signature));
+ if (sni_hostname)
+ return rb_str_new2 (sni_hostname);
+
+ return Qnil;
+}
+#else
+static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature UNUSED)
+{
+ return Qnil;
+}
+#endif
+
+/**************
+t_get_peername
+**************/
+
+static VALUE t_get_peername (VALUE self UNUSED, VALUE signature)
+{
+ char buf[1024];
+ socklen_t len = sizeof buf;
+ try {
+ if (evma_get_peername (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) {
+ return rb_str_new (buf, len);
+ }
+ } catch (std::runtime_error e) {
+ rb_raise (rb_eRuntimeError, "%s", e.what());
+ }
+
+ return Qnil;
+}
+
+/**************
+t_get_sockname
+**************/
+
+static VALUE t_get_sockname (VALUE self UNUSED, VALUE signature)
+{
+ char buf[1024];
+ socklen_t len = sizeof buf;
+ try {
+ if (evma_get_sockname (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) {
+ return rb_str_new (buf, len);
+ }
+ } catch (std::runtime_error e) {
+ rb_raise (rb_eRuntimeError, "%s", e.what());
+ }
+
+ return Qnil;
+}
+
+/********************
+t_get_subprocess_pid
+********************/
+
+static VALUE t_get_subprocess_pid (VALUE self UNUSED, VALUE signature)
+{
+ pid_t pid;
+ if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) {
+ return INT2NUM (pid);
+ }
+
+ return Qnil;
+}
+
+/***********************
+t_get_subprocess_status
+***********************/
+
+static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature)
+{
+ VALUE proc_status = Qnil;
+
+ int status;
+ pid_t pid;
+
+ if (evma_get_subprocess_status (NUM2BSIG (signature), &status)) {
+ if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) {
+ proc_status = rb_obj_alloc(rb_cProcStatus);
+
+ /* MRI Ruby uses hidden instance vars */
+ rb_iv_set(proc_status, "status", INT2FIX(status));
+ rb_iv_set(proc_status, "pid", INT2FIX(pid));
+
+#ifdef RUBINIUS
+ /* Rubinius uses standard instance vars */
+ rb_iv_set(proc_status, "@pid", INT2FIX(pid));
+ if (WIFEXITED(status)) {
+ rb_iv_set(proc_status, "@status", INT2FIX(WEXITSTATUS(status)));
+ } else if (WIFSIGNALED(status)) {
+ rb_iv_set(proc_status, "@termsig", INT2FIX(WTERMSIG(status)));
+ } else if (WIFSTOPPED(status)) {
+ rb_iv_set(proc_status, "@stopsig", INT2FIX(WSTOPSIG(status)));
+ }
+#endif
+ }
+ }
+
+ return proc_status;
+}
+
+/**********************
+t_get_connection_count
+**********************/
+
+static VALUE t_get_connection_count (VALUE self UNUSED)
+{
+ return INT2NUM(evma_get_connection_count());
+}
+
+/*****************************
+t_get_comm_inactivity_timeout
+*****************************/
+
+static VALUE t_get_comm_inactivity_timeout (VALUE self UNUSED, VALUE signature)
+{
+ return rb_float_new(evma_get_comm_inactivity_timeout(NUM2BSIG (signature)));
+}
+
+/*****************************
+t_set_comm_inactivity_timeout
+*****************************/
+
+static VALUE t_set_comm_inactivity_timeout (VALUE self UNUSED, VALUE signature, VALUE timeout)
+{
+ float ti = RFLOAT_VALUE(timeout);
+ if (evma_set_comm_inactivity_timeout(NUM2BSIG(signature), ti)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*****************************
+t_get_pending_connect_timeout
+*****************************/
+
+static VALUE t_get_pending_connect_timeout (VALUE self UNUSED, VALUE signature)
+{
+ return rb_float_new(evma_get_pending_connect_timeout(NUM2BSIG (signature)));
+}
+
+/*****************************
+t_set_pending_connect_timeout
+*****************************/
+
+static VALUE t_set_pending_connect_timeout (VALUE self UNUSED, VALUE signature, VALUE timeout)
+{
+ float ti = RFLOAT_VALUE(timeout);
+ if (evma_set_pending_connect_timeout(NUM2BSIG(signature), ti)) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/***************
+t_send_datagram
+***************/
+
+static VALUE t_send_datagram (VALUE self UNUSED, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port)
+{
+ int b = evma_send_datagram (NUM2BSIG (signature), StringValuePtr (data), FIX2INT (data_length), StringValueCStr(address), FIX2INT(port));
+ if (b < 0)
+ rb_raise (EM_eConnectionError, "%s", "error in sending datagram"); // FIXME: this could be more specific.
+ return INT2NUM (b);
+}
+
+
+/******************
+t_close_connection
+******************/
+
+static VALUE t_close_connection (VALUE self UNUSED, VALUE signature, VALUE after_writing)
+{
+ evma_close_connection (NUM2BSIG (signature), ((after_writing == Qtrue) ? 1 : 0));
+ return Qnil;
+}
+
+/********************************
+t_report_connection_error_status
+********************************/
+
+static VALUE t_report_connection_error_status (VALUE self UNUSED, VALUE signature)
+{
+ int b = evma_report_connection_error_status (NUM2BSIG (signature));
+ return INT2NUM (b);
+}
+
+
+
+/****************
+t_connect_server
+****************/
+
+static VALUE t_connect_server (VALUE self UNUSED, VALUE server, VALUE port)
+{
+ // Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
+ // Specifically, if the value of port comes in as a string rather than an integer,
+ // NUM2INT will throw a type error, but FIX2INT will generate garbage.
+
+ try {
+ const uintptr_t f = evma_connect_to_server (NULL, 0, StringValueCStr(server), NUM2INT(port));
+ if (!f)
+ rb_raise (EM_eConnectionError, "%s", "no connection");
+ return BSIG2NUM (f);
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+/*********************
+t_bind_connect_server
+*********************/
+
+static VALUE t_bind_connect_server (VALUE self UNUSED, VALUE bind_addr, VALUE bind_port, VALUE server, VALUE port)
+{
+ // Avoid FIX2INT in this case, because it doesn't deal with type errors properly.
+ // Specifically, if the value of port comes in as a string rather than an integer,
+ // NUM2INT will throw a type error, but FIX2INT will generate garbage.
+
+ try {
+ const uintptr_t f = evma_connect_to_server (StringValueCStr(bind_addr), NUM2INT(bind_port), StringValueCStr(server), NUM2INT(port));
+ if (!f)
+ rb_raise (EM_eConnectionError, "%s", "no connection");
+ return BSIG2NUM (f);
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+/*********************
+t_connect_unix_server
+*********************/
+
+static VALUE t_connect_unix_server (VALUE self UNUSED, VALUE serversocket)
+{
+ const uintptr_t f = evma_connect_to_unix_server (StringValueCStr(serversocket));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no connection");
+ return BSIG2NUM (f);
+}
+
+/***********
+t_attach_fd
+***********/
+
+static VALUE t_attach_fd (VALUE self UNUSED, VALUE file_descriptor, VALUE watch_mode)
+{
+ const uintptr_t f = evma_attach_fd (NUM2INT(file_descriptor), watch_mode == Qtrue);
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no connection");
+ return BSIG2NUM (f);
+}
+
+/***********
+t_detach_fd
+***********/
+
+static VALUE t_detach_fd (VALUE self UNUSED, VALUE signature)
+{
+ return INT2NUM(evma_detach_fd (NUM2BSIG (signature)));
+}
+
+/*********************
+t_get_file_descriptor
+*********************/
+static VALUE t_get_file_descriptor (VALUE self UNUSED, VALUE signature)
+{
+ return INT2NUM(evma_get_file_descriptor (NUM2BSIG (signature)));
+}
+
+/**************
+t_get_sock_opt
+**************/
+
+static VALUE t_get_sock_opt (VALUE self UNUSED, VALUE signature, VALUE lev, VALUE optname)
+{
+ int fd = evma_get_file_descriptor (NUM2BSIG (signature));
+ int level = NUM2INT(lev), option = NUM2INT(optname);
+ socklen_t len = 128;
+ char buf[128];
+
+ if (getsockopt(fd, level, option, buf, &len) < 0)
+ rb_sys_fail("getsockopt");
+
+ return rb_str_new(buf, len);
+}
+
+/**************
+t_set_sock_opt
+**************/
+
+static VALUE t_set_sock_opt (VALUE self UNUSED, VALUE signature, VALUE lev, VALUE optname, VALUE optval)
+{
+ int fd = evma_get_file_descriptor (NUM2BSIG (signature));
+ int level = NUM2INT(lev), option = NUM2INT(optname);
+ int i;
+ const void *v;
+ socklen_t len;
+
+ switch (TYPE(optval)) {
+ case T_FIXNUM:
+ i = FIX2INT(optval);
+ goto numval;
+ case T_FALSE:
+ i = 0;
+ goto numval;
+ case T_TRUE:
+ i = 1;
+ numval:
+ v = (void*)&i; len = sizeof(i);
+ break;
+ default:
+ StringValue(optval);
+ v = RSTRING_PTR(optval);
+ len = RSTRING_LENINT(optval);
+ break;
+ }
+
+
+ if (setsockopt(fd, level, option, (char *)v, len) < 0)
+ rb_sys_fail("setsockopt");
+
+ return INT2FIX(0);
+}
+
+/********************
+t_is_notify_readable
+********************/
+
+static VALUE t_is_notify_readable (VALUE self UNUSED, VALUE signature)
+{
+ return evma_is_notify_readable(NUM2BSIG (signature)) ? Qtrue : Qfalse;
+}
+
+/*********************
+t_set_notify_readable
+*********************/
+
+static VALUE t_set_notify_readable (VALUE self UNUSED, VALUE signature, VALUE mode)
+{
+ evma_set_notify_readable(NUM2BSIG(signature), mode == Qtrue);
+ return Qnil;
+}
+
+/********************
+t_is_notify_readable
+********************/
+
+static VALUE t_is_notify_writable (VALUE self UNUSED, VALUE signature)
+{
+ return evma_is_notify_writable(NUM2BSIG (signature)) ? Qtrue : Qfalse;
+}
+
+/*********************
+t_set_notify_writable
+*********************/
+
+static VALUE t_set_notify_writable (VALUE self UNUSED, VALUE signature, VALUE mode)
+{
+ evma_set_notify_writable(NUM2BSIG (signature), mode == Qtrue);
+ return Qnil;
+}
+
+/*******
+t_pause
+*******/
+
+static VALUE t_pause (VALUE self UNUSED, VALUE signature)
+{
+ return evma_pause(NUM2BSIG (signature)) ? Qtrue : Qfalse;
+}
+
+/********
+t_resume
+********/
+
+static VALUE t_resume (VALUE self UNUSED, VALUE signature)
+{
+ return evma_resume(NUM2BSIG (signature)) ? Qtrue : Qfalse;
+}
+
+/**********
+t_paused_p
+**********/
+
+static VALUE t_paused_p (VALUE self UNUSED, VALUE signature)
+{
+ return evma_is_paused(NUM2BSIG (signature)) ? Qtrue : Qfalse;
+}
+
+/*********************
+t_num_close_scheduled
+*********************/
+
+static VALUE t_num_close_scheduled (VALUE self UNUSED)
+{
+ return INT2FIX(evma_num_close_scheduled());
+}
+
+/*****************
+t_open_udp_socket
+*****************/
+
+static VALUE t_open_udp_socket (VALUE self UNUSED, VALUE server, VALUE port)
+{
+ const uintptr_t f = evma_open_datagram_socket (StringValueCStr(server), FIX2INT(port));
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no datagram socket");
+ return BSIG2NUM(f);
+}
+
+
+
+/*****************
+t_release_machine
+*****************/
+
+static VALUE t_release_machine (VALUE self UNUSED)
+{
+ evma_release_library();
+ return Qnil;
+}
+
+
+/******
+t_stop
+******/
+
+static VALUE t_stop (VALUE self UNUSED)
+{
+ evma_stop_machine();
+ return Qnil;
+}
+
+/******************
+t_signal_loopbreak
+******************/
+
+static VALUE t_signal_loopbreak (VALUE self UNUSED)
+{
+ evma_signal_loopbreak();
+ return Qnil;
+}
+
+/**************
+t_library_type
+**************/
+
+static VALUE t_library_type (VALUE self UNUSED)
+{
+ return rb_eval_string (":extension");
+}
+
+
+
+/*******************
+t_set_timer_quantum
+*******************/
+
+static VALUE t_set_timer_quantum (VALUE self UNUSED, VALUE interval)
+{
+ evma_set_timer_quantum (FIX2INT (interval));
+ return Qnil;
+}
+
+/********************
+t_get_max_timer_count
+********************/
+
+static VALUE t_get_max_timer_count (VALUE self UNUSED)
+{
+ return INT2FIX (evma_get_max_timer_count());
+}
+
+/********************
+t_set_max_timer_count
+********************/
+
+static VALUE t_set_max_timer_count (VALUE self UNUSED, VALUE ct)
+{
+ evma_set_max_timer_count (FIX2INT (ct));
+ return Qnil;
+}
+
+/********************
+t_get/set_simultaneous_accept_count
+********************/
+
+static VALUE t_get_simultaneous_accept_count (VALUE self UNUSED)
+{
+ return INT2FIX (evma_get_simultaneous_accept_count());
+}
+
+static VALUE t_set_simultaneous_accept_count (VALUE self UNUSED, VALUE ct)
+{
+ evma_set_simultaneous_accept_count (FIX2INT (ct));
+ return Qnil;
+}
+
+/***************
+t_setuid_string
+***************/
+
+static VALUE t_setuid_string (VALUE self UNUSED, VALUE username)
+{
+ evma_setuid_string (StringValueCStr (username));
+ return Qnil;
+}
+
+
+
+/**************
+t_invoke_popen
+**************/
+
+static VALUE t_invoke_popen (VALUE self UNUSED, VALUE cmd)
+{
+ #ifdef OS_WIN32
+ rb_raise (EM_eUnsupported, "popen is not available on this platform");
+ #endif
+
+ int len = RARRAY_LEN(cmd);
+ if (len >= 2048)
+ rb_raise (rb_eRuntimeError, "%s", "too many arguments to popen");
+ char *strings [2048];
+ for (int i=0; i < len; i++) {
+ VALUE ix = INT2FIX (i);
+ VALUE s = rb_ary_aref (1, &ix, cmd);
+ strings[i] = StringValueCStr (s);
+ }
+ strings[len] = NULL;
+
+ uintptr_t f = 0;
+ try {
+ f = evma_popen (strings);
+ } catch (std::runtime_error e) {
+ rb_raise (rb_eRuntimeError, "%s", e.what());
+ }
+ if (!f) {
+ char *err = strerror (errno);
+ char buf[100];
+ memset (buf, 0, sizeof(buf));
+ snprintf (buf, sizeof(buf)-1, "no popen: %s", (err?err:"???"));
+ rb_raise (rb_eRuntimeError, "%s", buf);
+ }
+ return BSIG2NUM (f);
+}
+
+
+/***************
+t_read_keyboard
+***************/
+
+static VALUE t_read_keyboard (VALUE self UNUSED)
+{
+ const uintptr_t f = evma_open_keyboard();
+ if (!f)
+ rb_raise (rb_eRuntimeError, "%s", "no keyboard reader");
+ return BSIG2NUM (f);
+}
+
+
+/****************
+t_watch_filename
+****************/
+
+static VALUE t_watch_filename (VALUE self UNUSED, VALUE fname)
+{
+ try {
+ return BSIG2NUM(evma_watch_filename(StringValueCStr(fname)));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eUnsupported, "%s", e.what());
+ }
+ return Qnil;
+}
+
+
+/******************
+t_unwatch_filename
+******************/
+
+static VALUE t_unwatch_filename (VALUE self UNUSED, VALUE sig)
+{
+ try {
+ evma_unwatch_filename(NUM2BSIG (sig));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eInvalidSignature, "%s", e.what());
+ }
+
+ return Qnil;
+}
+
+
+/***********
+t_watch_pid
+***********/
+
+static VALUE t_watch_pid (VALUE self UNUSED, VALUE pid)
+{
+ try {
+ return BSIG2NUM(evma_watch_pid(NUM2INT(pid)));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eUnsupported, "%s", e.what());
+ }
+ return Qnil;
+}
+
+
+/*************
+t_unwatch_pid
+*************/
+
+static VALUE t_unwatch_pid (VALUE self UNUSED, VALUE sig)
+{
+ evma_unwatch_pid(NUM2BSIG (sig));
+ return Qnil;
+}
+
+
+/**********
+t__epoll_p
+**********/
+
+static VALUE t__epoll_p (VALUE self UNUSED)
+{
+ #ifdef HAVE_EPOLL
+ return Qtrue;
+ #else
+ return Qfalse;
+ #endif
+}
+
+/********
+t__epoll
+********/
+
+static VALUE t__epoll (VALUE self UNUSED)
+{
+ if (t__epoll_p(self) == Qfalse)
+ return Qfalse;
+
+ evma_set_epoll (1);
+ return Qtrue;
+}
+
+/***********
+t__epoll_set
+***********/
+
+static VALUE t__epoll_set (VALUE self, VALUE val)
+{
+ if (t__epoll_p(self) == Qfalse && val == Qtrue)
+ rb_raise (EM_eUnsupported, "%s", "epoll is not supported on this platform");
+
+ evma_set_epoll (val == Qtrue ? 1 : 0);
+ return val;
+}
+
+
+/***********
+t__kqueue_p
+***********/
+
+static VALUE t__kqueue_p (VALUE self UNUSED)
+{
+ #ifdef HAVE_KQUEUE
+ return Qtrue;
+ #else
+ return Qfalse;
+ #endif
+}
+
+/*********
+t__kqueue
+*********/
+
+static VALUE t__kqueue (VALUE self UNUSED)
+{
+ if (t__kqueue_p(self) == Qfalse)
+ return Qfalse;
+
+ evma_set_kqueue (1);
+ return Qtrue;
+}
+
+/*************
+t__kqueue_set
+*************/
+
+static VALUE t__kqueue_set (VALUE self, VALUE val)
+{
+ if (t__kqueue_p(self) == Qfalse && val == Qtrue)
+ rb_raise (EM_eUnsupported, "%s", "kqueue is not supported on this platform");
+
+ evma_set_kqueue (val == Qtrue ? 1 : 0);
+ return val;
+}
+
+
+/********
+t__ssl_p
+********/
+
+static VALUE t__ssl_p (VALUE self UNUSED)
+{
+ #ifdef WITH_SSL
+ return Qtrue;
+ #else
+ return Qfalse;
+ #endif
+}
+
+/********
+t_stopping
+********/
+
+static VALUE t_stopping ()
+{
+ if (evma_stopping()) {
+ return Qtrue;
+ } else {
+ return Qfalse;
+ }
+}
+
+
+/****************
+t_send_file_data
+****************/
+
+static VALUE t_send_file_data (VALUE self UNUSED, VALUE signature, VALUE filename)
+{
+
+ /* The current implementation of evma_send_file_data_to_connection enforces a strict
+ * upper limit on the file size it will transmit (currently 32K). The function returns
+ * zero on success, -1 if the requested file exceeds its size limit, and a positive
+ * number for other errors.
+ * TODO: Positive return values are actually errno's, which is probably the wrong way to
+ * do this. For one thing it's ugly. For another, we can't be sure zero is never a real errno.
+ */
+
+ int b = evma_send_file_data_to_connection (NUM2BSIG (signature), StringValueCStr(filename));
+ if (b == -1)
+ rb_raise(rb_eRuntimeError, "%s", "File too large. send_file_data() supports files under 32k.");
+ if (b > 0) {
+ char *err = strerror (b);
+ char buf[1024];
+ memset (buf, 0, sizeof(buf));
+ snprintf (buf, sizeof(buf)-1, ": %s %s", StringValueCStr(filename),(err?err:"???"));
+
+ rb_raise (rb_eIOError, "%s", buf);
+ }
+
+ return INT2NUM (0);
+}
+
+
+/*******************
+t_set_rlimit_nofile
+*******************/
+
+static VALUE t_set_rlimit_nofile (VALUE self UNUSED, VALUE arg)
+{
+ arg = (NIL_P(arg)) ? -1 : NUM2INT (arg);
+ return INT2NUM (evma_set_rlimit_nofile (arg));
+}
+
+/***************************
+conn_get_outbound_data_size
+***************************/
+
+static VALUE conn_get_outbound_data_size (VALUE self)
+{
+ VALUE sig = rb_ivar_get (self, Intern_at_signature);
+ return INT2NUM (evma_get_outbound_data_size (NUM2BSIG (sig)));
+}
+
+
+/******************************
+conn_associate_callback_target
+******************************/
+
+static VALUE conn_associate_callback_target (VALUE self UNUSED, VALUE sig UNUSED)
+{
+ // No-op for the time being.
+ return Qnil;
+}
+
+
+/***************
+t_get_loop_time
+****************/
+
+static VALUE t_get_loop_time (VALUE self UNUSED)
+{
+ uint64_t current_time = evma_get_current_loop_time();
+ if (current_time == 0) {
+ return Qnil;
+ }
+
+ // Generally the industry has moved to 64-bit time_t, this is just in case we're 32-bit time_t.
+ if (sizeof(time_t) < 8 && current_time > INT_MAX) {
+ return rb_funcall(rb_cTime, Intern_at, 2, INT2NUM(current_time / 1000000), INT2NUM(current_time % 1000000));
+ } else {
+ return rb_time_new(current_time / 1000000, current_time % 1000000);
+ }
+}
+
+
+/*************
+t_start_proxy
+**************/
+
+static VALUE t_start_proxy (VALUE self UNUSED, VALUE from, VALUE to, VALUE bufsize, VALUE length)
+{
+ try {
+ evma_start_proxy(NUM2BSIG (from), NUM2BSIG (to), NUM2ULONG(bufsize), NUM2ULONG(length));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+
+/************
+t_stop_proxy
+*************/
+
+static VALUE t_stop_proxy (VALUE self UNUSED, VALUE from)
+{
+ try{
+ evma_stop_proxy(NUM2BSIG (from));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+/***************
+t_proxied_bytes
+****************/
+
+static VALUE t_proxied_bytes (VALUE self UNUSED, VALUE from)
+{
+ try{
+ return BSIG2NUM(evma_proxied_bytes(NUM2BSIG (from)));
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+/***************
+t_get_idle_time
+****************/
+
+static VALUE t_get_idle_time (VALUE self UNUSED, VALUE from)
+{
+ try{
+ uint64_t current_time = evma_get_current_loop_time();
+ uint64_t time = evma_get_last_activity_time(NUM2BSIG (from));
+ if (current_time != 0 && time != 0) {
+ if (time >= current_time)
+ return BSIG2NUM(0);
+ else {
+ uint64_t diff = current_time - time;
+ float seconds = diff / (1000.0*1000.0);
+ return rb_float_new(seconds);
+ }
+ return Qnil;
+ }
+ } catch (std::runtime_error e) {
+ rb_raise (EM_eConnectionError, "%s", e.what());
+ }
+ return Qnil;
+}
+
+/************************
+t_get_heartbeat_interval
+*************************/
+
+static VALUE t_get_heartbeat_interval (VALUE self UNUSED)
+{
+ return rb_float_new(evma_get_heartbeat_interval());
+}
+
+
+/************************
+t_set_heartbeat_interval
+*************************/
+
+static VALUE t_set_heartbeat_interval (VALUE self UNUSED, VALUE interval)
+{
+ float iv = RFLOAT_VALUE(interval);
+ if (evma_set_heartbeat_interval(iv))
+ return Qtrue;
+ return Qfalse;
+}
+
+
+/*********************
+Init_rubyeventmachine
+*********************/
+
+extern "C" void Init_rubyeventmachine()
+{
+ // Lookup Process::Status for get_subprocess_status
+ VALUE rb_mProcess = rb_const_get(rb_cObject, rb_intern("Process"));
+ rb_cProcStatus = rb_const_get(rb_mProcess, rb_intern("Status"));
+
+ // Tuck away some symbol values so we don't have to look 'em up every time we need 'em.
+ Intern_at_signature = rb_intern ("@signature");
+ Intern_at_timers = rb_intern ("@timers");
+ Intern_at_conns = rb_intern ("@conns");
+ Intern_at_error_handler = rb_intern("@error_handler");
+
+ Intern_event_callback = rb_intern ("event_callback");
+ Intern_run_deferred_callbacks = rb_intern ("run_deferred_callbacks");
+ Intern_delete = rb_intern ("delete");
+ Intern_call = rb_intern ("call");
+ Intern_at = rb_intern("at");
+ Intern_receive_data = rb_intern ("receive_data");
+ Intern_ssl_handshake_completed = rb_intern ("ssl_handshake_completed");
+ Intern_ssl_verify_peer = rb_intern ("ssl_verify_peer");
+ Intern_notify_readable = rb_intern ("notify_readable");
+ Intern_notify_writable = rb_intern ("notify_writable");
+ Intern_proxy_target_unbound = rb_intern ("proxy_target_unbound");
+ Intern_proxy_completed = rb_intern ("proxy_completed");
+ Intern_connection_completed = rb_intern ("connection_completed");
+
+ // INCOMPLETE, we need to define class Connections inside module EventMachine
+ // run_machine and run_machine_without_threads are now identical.
+ // Must deprecate the without_threads variant.
+ EmModule = rb_define_module ("EventMachine");
+ EmConnection = rb_define_class_under (EmModule, "Connection", rb_cObject);
+
+ rb_define_class_under (EmModule, "NoHandlerForAcceptedConnection", rb_eRuntimeError);
+ EM_eConnectionError = rb_define_class_under (EmModule, "ConnectionError", rb_eRuntimeError);
+ EM_eConnectionNotBound = rb_define_class_under (EmModule, "ConnectionNotBound", rb_eRuntimeError);
+ EM_eUnknownTimerFired = rb_define_class_under (EmModule, "UnknownTimerFired", rb_eRuntimeError);
+ EM_eUnsupported = rb_define_class_under (EmModule, "Unsupported", rb_eRuntimeError);
+ EM_eInvalidSignature = rb_define_class_under (EmModule, "InvalidSignature", rb_eRuntimeError);
+
+ rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0);
+ rb_define_module_function (EmModule, "run_machine_once", (VALUE(*)(...))t_run_machine_once, 0);
+ rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine, 0);
+ rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine, 0);
+ rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1);
+ rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2);
+ rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1);
+ rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1);
+ rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1);
+ rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 10);
+ rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1);
+ rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1);
+ rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1);
+ rb_define_module_function (EmModule, "get_cipher_name", (VALUE(*)(...))t_get_cipher_name, 1);
+ rb_define_module_function (EmModule, "get_cipher_protocol", (VALUE(*)(...))t_get_cipher_protocol, 1);
+ rb_define_module_function (EmModule, "get_sni_hostname", (VALUE(*)(...))t_get_sni_hostname, 1);
+ rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3);
+ rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5);
+ rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2);
+ rb_define_module_function (EmModule, "report_connection_error_status", (VALUE(*)(...))t_report_connection_error_status, 1);
+ rb_define_module_function (EmModule, "connect_server", (VALUE(*)(...))t_connect_server, 2);
+ rb_define_module_function (EmModule, "bind_connect_server", (VALUE(*)(...))t_bind_connect_server, 4);
+ rb_define_module_function (EmModule, "connect_unix_server", (VALUE(*)(...))t_connect_unix_server, 1);
+
+ rb_define_module_function (EmModule, "attach_fd", (VALUE (*)(...))t_attach_fd, 2);
+ rb_define_module_function (EmModule, "detach_fd", (VALUE (*)(...))t_detach_fd, 1);
+ rb_define_module_function (EmModule, "get_file_descriptor", (VALUE (*)(...))t_get_file_descriptor, 1);
+ rb_define_module_function (EmModule, "get_sock_opt", (VALUE (*)(...))t_get_sock_opt, 3);
+ rb_define_module_function (EmModule, "set_sock_opt", (VALUE (*)(...))t_set_sock_opt, 4);
+ rb_define_module_function (EmModule, "set_notify_readable", (VALUE (*)(...))t_set_notify_readable, 2);
+ rb_define_module_function (EmModule, "set_notify_writable", (VALUE (*)(...))t_set_notify_writable, 2);
+ rb_define_module_function (EmModule, "is_notify_readable", (VALUE (*)(...))t_is_notify_readable, 1);
+ rb_define_module_function (EmModule, "is_notify_writable", (VALUE (*)(...))t_is_notify_writable, 1);
+
+ rb_define_module_function (EmModule, "pause_connection", (VALUE (*)(...))t_pause, 1);
+ rb_define_module_function (EmModule, "resume_connection", (VALUE (*)(...))t_resume, 1);
+ rb_define_module_function (EmModule, "connection_paused?", (VALUE (*)(...))t_paused_p, 1);
+ rb_define_module_function (EmModule, "num_close_scheduled", (VALUE (*)(...))t_num_close_scheduled, 0);
+
+ rb_define_module_function (EmModule, "start_proxy", (VALUE (*)(...))t_start_proxy, 4);
+ rb_define_module_function (EmModule, "stop_proxy", (VALUE (*)(...))t_stop_proxy, 1);
+ rb_define_module_function (EmModule, "get_proxied_bytes", (VALUE (*)(...))t_proxied_bytes, 1);
+
+ rb_define_module_function (EmModule, "watch_filename", (VALUE (*)(...))t_watch_filename, 1);
+ rb_define_module_function (EmModule, "unwatch_filename", (VALUE (*)(...))t_unwatch_filename, 1);
+
+ rb_define_module_function (EmModule, "watch_pid", (VALUE (*)(...))t_watch_pid, 1);
+ rb_define_module_function (EmModule, "unwatch_pid", (VALUE (*)(...))t_unwatch_pid, 1);
+
+ rb_define_module_function (EmModule, "current_time", (VALUE(*)(...))t_get_loop_time, 0);
+
+ rb_define_module_function (EmModule, "open_udp_socket", (VALUE(*)(...))t_open_udp_socket, 2);
+ rb_define_module_function (EmModule, "read_keyboard", (VALUE(*)(...))t_read_keyboard, 0);
+ rb_define_module_function (EmModule, "release_machine", (VALUE(*)(...))t_release_machine, 0);
+ rb_define_module_function (EmModule, "stop", (VALUE(*)(...))t_stop, 0);
+ rb_define_module_function (EmModule, "signal_loopbreak", (VALUE(*)(...))t_signal_loopbreak, 0);
+ rb_define_module_function (EmModule, "library_type", (VALUE(*)(...))t_library_type, 0);
+ rb_define_module_function (EmModule, "set_timer_quantum", (VALUE(*)(...))t_set_timer_quantum, 1);
+ rb_define_module_function (EmModule, "get_max_timer_count", (VALUE(*)(...))t_get_max_timer_count, 0);
+ rb_define_module_function (EmModule, "set_max_timer_count", (VALUE(*)(...))t_set_max_timer_count, 1);
+ rb_define_module_function (EmModule, "get_simultaneous_accept_count", (VALUE(*)(...))t_get_simultaneous_accept_count, 0);
+ rb_define_module_function (EmModule, "set_simultaneous_accept_count", (VALUE(*)(...))t_set_simultaneous_accept_count, 1);
+ rb_define_module_function (EmModule, "setuid_string", (VALUE(*)(...))t_setuid_string, 1);
+ rb_define_module_function (EmModule, "invoke_popen", (VALUE(*)(...))t_invoke_popen, 1);
+ rb_define_module_function (EmModule, "send_file_data", (VALUE(*)(...))t_send_file_data, 2);
+ rb_define_module_function (EmModule, "get_heartbeat_interval", (VALUE(*)(...))t_get_heartbeat_interval, 0);
+ rb_define_module_function (EmModule, "set_heartbeat_interval", (VALUE(*)(...))t_set_heartbeat_interval, 1);
+ rb_define_module_function (EmModule, "get_idle_time", (VALUE(*)(...))t_get_idle_time, 1);
+
+ rb_define_module_function (EmModule, "get_peername", (VALUE(*)(...))t_get_peername, 1);
+ rb_define_module_function (EmModule, "get_sockname", (VALUE(*)(...))t_get_sockname, 1);
+ rb_define_module_function (EmModule, "get_subprocess_pid", (VALUE(*)(...))t_get_subprocess_pid, 1);
+ rb_define_module_function (EmModule, "get_subprocess_status", (VALUE(*)(...))t_get_subprocess_status, 1);
+ rb_define_module_function (EmModule, "get_comm_inactivity_timeout", (VALUE(*)(...))t_get_comm_inactivity_timeout, 1);
+ rb_define_module_function (EmModule, "set_comm_inactivity_timeout", (VALUE(*)(...))t_set_comm_inactivity_timeout, 2);
+ rb_define_module_function (EmModule, "get_pending_connect_timeout", (VALUE(*)(...))t_get_pending_connect_timeout, 1);
+ rb_define_module_function (EmModule, "set_pending_connect_timeout", (VALUE(*)(...))t_set_pending_connect_timeout, 2);
+ rb_define_module_function (EmModule, "set_rlimit_nofile", (VALUE(*)(...))t_set_rlimit_nofile, 1);
+ rb_define_module_function (EmModule, "get_connection_count", (VALUE(*)(...))t_get_connection_count, 0);
+
+ rb_define_module_function (EmModule, "epoll", (VALUE(*)(...))t__epoll, 0);
+ rb_define_module_function (EmModule, "epoll=", (VALUE(*)(...))t__epoll_set, 1);
+ rb_define_module_function (EmModule, "epoll?", (VALUE(*)(...))t__epoll_p, 0);
+
+ rb_define_module_function (EmModule, "kqueue", (VALUE(*)(...))t__kqueue, 0);
+ rb_define_module_function (EmModule, "kqueue=", (VALUE(*)(...))t__kqueue_set, 1);
+ rb_define_module_function (EmModule, "kqueue?", (VALUE(*)(...))t__kqueue_p, 0);
+
+ rb_define_module_function (EmModule, "ssl?", (VALUE(*)(...))t__ssl_p, 0);
+ rb_define_module_function(EmModule, "stopping?",(VALUE(*)(...))t_stopping, 0);
+
+ rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0);
+ rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1);
+
+ // Connection states
+ rb_define_const (EmModule, "TimerFired", INT2NUM(EM_TIMER_FIRED ));
+ rb_define_const (EmModule, "ConnectionData", INT2NUM(EM_CONNECTION_READ ));
+ rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(EM_CONNECTION_UNBOUND ));
+ rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(EM_CONNECTION_ACCEPTED ));
+ rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(EM_CONNECTION_COMPLETED ));
+ rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(EM_LOOPBREAK_SIGNAL ));
+ rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(EM_CONNECTION_NOTIFY_READABLE));
+ rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(EM_CONNECTION_NOTIFY_WRITABLE));
+ rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(EM_SSL_HANDSHAKE_COMPLETED ));
+ rb_define_const (EmModule, "SslVerify", INT2NUM(EM_SSL_VERIFY ));
+ // EM_PROXY_TARGET_UNBOUND = 110,
+ // EM_PROXY_COMPLETED = 111
+
+ // SSL Protocols
+ rb_define_const (EmModule, "EM_PROTO_SSLv2", INT2NUM(EM_PROTO_SSLv2 ));
+ rb_define_const (EmModule, "EM_PROTO_SSLv3", INT2NUM(EM_PROTO_SSLv3 ));
+ rb_define_const (EmModule, "EM_PROTO_TLSv1", INT2NUM(EM_PROTO_TLSv1 ));
+ rb_define_const (EmModule, "EM_PROTO_TLSv1_1", INT2NUM(EM_PROTO_TLSv1_1));
+ rb_define_const (EmModule, "EM_PROTO_TLSv1_2", INT2NUM(EM_PROTO_TLSv1_2));
+}
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.cpp b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.cpp
new file mode 100644
index 0000000..8d5e038
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.cpp
@@ -0,0 +1,619 @@
+/*****************************************************************************
+
+$Id$
+
+File: ssl.cpp
+Date: 30Apr06
+
+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.
+
+*****************************************************************************/
+
+
+#ifdef WITH_SSL
+
+#include "project.h"
+
+
+bool SslContext_t::bLibraryInitialized = false;
+
+
+
+static void InitializeDefaultCredentials();
+static EVP_PKEY *DefaultPrivateKey = NULL;
+static X509 *DefaultCertificate = NULL;
+
+static char PrivateMaterials[] = {
+"-----BEGIN RSA PRIVATE KEY-----\n"
+"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n"
+"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n"
+"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n"
+"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n"
+"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n"
+"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n"
+"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n"
+"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n"
+"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n"
+"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n"
+"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n"
+"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n"
+"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n"
+"-----END RSA PRIVATE KEY-----\n"
+"-----BEGIN CERTIFICATE-----\n"
+"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n"
+"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n"
+"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n"
+"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n"
+"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n"
+"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n"
+"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n"
+"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n"
+"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n"
+"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n"
+"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n"
+"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n"
+"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n"
+"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n"
+"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n"
+"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n"
+"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n"
+"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n"
+"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n"
+"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n"
+"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n"
+"-----END CERTIFICATE-----\n"};
+
+/* These private materials were made with:
+ * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500
+ * TODO: We need a full-blown capability to work with user-supplied
+ * keypairs and properly-signed certificates.
+ */
+
+
+/*****************
+builtin_passwd_cb
+*****************/
+
+extern "C" int builtin_passwd_cb (char *buf UNUSED, int bufsize UNUSED, int rwflag UNUSED, void *userdata UNUSED)
+{
+ strcpy (buf, "kittycat");
+ return 8;
+}
+
+/****************************
+InitializeDefaultCredentials
+****************************/
+
+static void InitializeDefaultCredentials()
+{
+ BIO *bio = BIO_new_mem_buf (PrivateMaterials, -1);
+ assert (bio);
+
+ if (DefaultPrivateKey) {
+ // we may come here in a restart.
+ EVP_PKEY_free (DefaultPrivateKey);
+ DefaultPrivateKey = NULL;
+ }
+ PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0);
+
+ if (DefaultCertificate) {
+ // we may come here in a restart.
+ X509_free (DefaultCertificate);
+ DefaultCertificate = NULL;
+ }
+ PEM_read_bio_X509 (bio, &DefaultCertificate, NULL, 0);
+
+ BIO_free (bio);
+}
+
+
+
+/**************************
+SslContext_t::SslContext_t
+**************************/
+
+SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) :
+ bIsServer (is_server),
+ pCtx (NULL),
+ PrivateKey (NULL),
+ Certificate (NULL)
+{
+ /* TODO: the usage of the specified private-key and cert-chain filenames only applies to
+ * client-side connections at this point. Server connections currently use the default materials.
+ * That needs to be fixed asap.
+ * Also, in this implementation, server-side connections use statically defined X-509 defaults.
+ * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY
+ * objects when we call our destructor, or whether just calling SSL_CTX_free is enough.
+ */
+
+ if (!bLibraryInitialized) {
+ bLibraryInitialized = true;
+ SSL_library_init();
+ OpenSSL_add_ssl_algorithms();
+ OpenSSL_add_all_algorithms();
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+
+ InitializeDefaultCredentials();
+ }
+
+ pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method());
+ if (!pCtx)
+ throw std::runtime_error ("no SSL context");
+
+ SSL_CTX_set_options (pCtx, SSL_OP_ALL);
+
+ #ifdef SSL_CTRL_CLEAR_OPTIONS
+ SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
+ # ifdef SSL_OP_NO_TLSv1_1
+ SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1);
+ # endif
+ # ifdef SSL_OP_NO_TLSv1_2
+ SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2);
+ # endif
+ #endif
+
+ if (!(ssl_version & EM_PROTO_SSLv2))
+ SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2);
+
+ if (!(ssl_version & EM_PROTO_SSLv3))
+ SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3);
+
+ if (!(ssl_version & EM_PROTO_TLSv1))
+ SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1);
+
+ #ifdef SSL_OP_NO_TLSv1_1
+ if (!(ssl_version & EM_PROTO_TLSv1_1))
+ SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1);
+ #endif
+
+ #ifdef SSL_OP_NO_TLSv1_2
+ if (!(ssl_version & EM_PROTO_TLSv1_2))
+ SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2);
+ #endif
+
+ #ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
+ #endif
+
+ if (bIsServer) {
+
+ // The SSL_CTX calls here do NOT allocate memory.
+ int e;
+ if (privkeyfile.length() > 0)
+ e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
+ else
+ e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey);
+ if (e <= 0) ERR_print_errors_fp(stderr);
+ assert (e > 0);
+
+ if (certchainfile.length() > 0)
+ e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
+ else
+ e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
+ if (e <= 0) ERR_print_errors_fp(stderr);
+ assert (e > 0);
+
+ if (dhparam.length() > 0) {
+ DH *dh;
+ BIO *bio;
+
+ bio = BIO_new_file(dhparam.c_str(), "r");
+ if (bio == NULL) {
+ char buf [500];
+ snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str());
+ throw std::runtime_error (buf);
+ }
+
+ dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+
+ if (dh == NULL) {
+ BIO_free(bio);
+ char buf [500];
+ snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str());
+ throw std::runtime_error (buf);
+ }
+
+ SSL_CTX_set_tmp_dh(pCtx, dh);
+
+ DH_free(dh);
+ BIO_free(bio);
+ }
+
+ if (ecdh_curve.length() > 0) {
+ #if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
+ int nid;
+ EC_KEY *ecdh;
+
+ nid = OBJ_sn2nid((const char *) ecdh_curve.c_str());
+ if (nid == 0) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str());
+ throw std::runtime_error (buf);
+ }
+
+ ecdh = EC_KEY_new_by_curve_name(nid);
+ if (ecdh == NULL) {
+ char buf [200];
+ snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str());
+ throw std::runtime_error (buf);
+ }
+
+ SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE);
+
+ SSL_CTX_set_tmp_ecdh(pCtx, ecdh);
+
+ EC_KEY_free(ecdh);
+ #else
+ throw std::runtime_error ("No openssl ECDH support");
+ #endif
+ }
+ }
+
+ if (cipherlist.length() > 0)
+ SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str());
+ else
+ SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
+
+ if (bIsServer) {
+ SSL_CTX_sess_set_cache_size (pCtx, 128);
+ SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
+ }
+ else {
+ int e;
+ if (privkeyfile.length() > 0) {
+ e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM);
+ if (e <= 0) ERR_print_errors_fp(stderr);
+ assert (e > 0);
+ }
+ if (certchainfile.length() > 0) {
+ e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str());
+ if (e <= 0) ERR_print_errors_fp(stderr);
+ assert (e > 0);
+ }
+ }
+}
+
+
+
+/***************************
+SslContext_t::~SslContext_t
+***************************/
+
+SslContext_t::~SslContext_t()
+{
+ if (pCtx)
+ SSL_CTX_free (pCtx);
+ if (PrivateKey)
+ EVP_PKEY_free (PrivateKey);
+ if (Certificate)
+ X509_free (Certificate);
+}
+
+
+
+/******************
+SslBox_t::SslBox_t
+******************/
+
+SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding):
+ bIsServer (is_server),
+ bHandshakeCompleted (false),
+ bVerifyPeer (verify_peer),
+ bFailIfNoPeerCert (fail_if_no_peer_cert),
+ pSSL (NULL),
+ pbioRead (NULL),
+ pbioWrite (NULL)
+{
+ /* TODO someday: make it possible to re-use SSL contexts so we don't have to create
+ * a new one every time we come here.
+ */
+
+ Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version);
+ assert (Context);
+
+ pbioRead = BIO_new (BIO_s_mem());
+ assert (pbioRead);
+
+ pbioWrite = BIO_new (BIO_s_mem());
+ assert (pbioWrite);
+
+ pSSL = SSL_new (Context->pCtx);
+ assert (pSSL);
+
+ if (snihostname.length() > 0) {
+ SSL_set_tlsext_host_name (pSSL, snihostname.c_str());
+ }
+
+ SSL_set_bio (pSSL, pbioRead, pbioWrite);
+
+ // Store a pointer to the binding signature in the SSL object so we can retrieve it later
+ SSL_set_ex_data(pSSL, 0, (void*) binding);
+
+ if (bVerifyPeer) {
+ int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
+ if (bFailIfNoPeerCert)
+ mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ SSL_set_verify(pSSL, mode, ssl_verify_wrapper);
+ }
+
+ if (!bIsServer) {
+ int e = SSL_connect (pSSL);
+ if (e != 1)
+ ERR_print_errors_fp(stderr);
+ }
+}
+
+
+
+/*******************
+SslBox_t::~SslBox_t
+*******************/
+
+SslBox_t::~SslBox_t()
+{
+ // Freeing pSSL will also free the associated BIOs, so DON'T free them separately.
+ if (pSSL) {
+ if (SSL_get_shutdown (pSSL) & SSL_RECEIVED_SHUTDOWN)
+ SSL_shutdown (pSSL);
+ else
+ SSL_clear (pSSL);
+ SSL_free (pSSL);
+ }
+
+ delete Context;
+}
+
+
+
+/***********************
+SslBox_t::PutCiphertext
+***********************/
+
+bool SslBox_t::PutCiphertext (const char *buf, int bufsize)
+{
+ assert (buf && (bufsize > 0));
+
+ assert (pbioRead);
+ int n = BIO_write (pbioRead, buf, bufsize);
+
+ return (n == bufsize) ? true : false;
+}
+
+
+/**********************
+SslBox_t::GetPlaintext
+**********************/
+
+int SslBox_t::GetPlaintext (char *buf, int bufsize)
+{
+ if (!SSL_is_init_finished (pSSL)) {
+ int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
+ if (e != 1) {
+ int er = SSL_get_error (pSSL, e);
+ if (er != SSL_ERROR_WANT_READ) {
+ ERR_print_errors_fp(stderr);
+ // Return -1 for a nonfatal error, -2 for an error that should force the connection down.
+ return (er == SSL_ERROR_SSL) ? (-2) : (-1);
+ }
+ else
+ return 0;
+ }
+ bHandshakeCompleted = true;
+ // If handshake finished, FALL THROUGH and return the available plaintext.
+ }
+
+ if (!SSL_is_init_finished (pSSL)) {
+ // We can get here if a browser abandons a handshake.
+ // The user can see a warning dialog and abort the connection.
+ //cerr << "<SSL_incomp>";
+ return 0;
+ }
+
+ //cerr << "CIPH: " << SSL_get_cipher (pSSL) << endl;
+
+ int n = SSL_read (pSSL, buf, bufsize);
+ if (n >= 0) {
+ return n;
+ }
+ else {
+ if (SSL_get_error (pSSL, n) == SSL_ERROR_WANT_READ) {
+ return 0;
+ }
+ else {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/**************************
+SslBox_t::CanGetCiphertext
+**************************/
+
+bool SslBox_t::CanGetCiphertext()
+{
+ assert (pbioWrite);
+ return BIO_pending (pbioWrite) ? true : false;
+}
+
+
+
+/***********************
+SslBox_t::GetCiphertext
+***********************/
+
+int SslBox_t::GetCiphertext (char *buf, int bufsize)
+{
+ assert (pbioWrite);
+ assert (buf && (bufsize > 0));
+
+ return BIO_read (pbioWrite, buf, bufsize);
+}
+
+
+
+/**********************
+SslBox_t::PutPlaintext
+**********************/
+
+int SslBox_t::PutPlaintext (const char *buf, int bufsize)
+{
+ // The caller will interpret the return value as the number of bytes written.
+ // WARNING WARNING WARNING, are there any situations in which a 0 or -1 return
+ // from SSL_write means we should immediately retry? The socket-machine loop
+ // will probably wait for a time-out cycle (perhaps a second) before re-trying.
+ // THIS WOULD CAUSE A PERCEPTIBLE DELAY!
+
+ /* We internally queue any outbound plaintext that can't be dispatched
+ * because we're in the middle of a handshake or something.
+ * When we get called, try to send any queued data first, and then
+ * send the caller's data (or queue it). We may get called with no outbound
+ * data, which means we try to send the outbound queue and that's all.
+ *
+ * Return >0 if we wrote any data, 0 if we didn't, and <0 for a fatal error.
+ * Note that if we return 0, the connection is still considered live
+ * and we are signalling that we have accepted the outbound data (if any).
+ */
+
+ OutboundQ.Push (buf, bufsize);
+
+ if (!SSL_is_init_finished (pSSL))
+ return 0;
+
+ bool fatal = false;
+ bool did_work = false;
+ int pending = BIO_pending(pbioWrite);
+
+ while (OutboundQ.HasPages() && pending < SSLBOX_WRITE_BUFFER_SIZE) {
+ const char *page;
+ int length;
+ OutboundQ.Front (&page, &length);
+ assert (page && (length > 0));
+ int n = SSL_write (pSSL, page, length);
+ pending = BIO_pending(pbioWrite);
+
+ if (n > 0) {
+ did_work = true;
+ OutboundQ.PopFront();
+ }
+ else {
+ int er = SSL_get_error (pSSL, n);
+ if ((er != SSL_ERROR_WANT_READ) && (er != SSL_ERROR_WANT_WRITE))
+ fatal = true;
+ break;
+ }
+ }
+
+
+ if (did_work)
+ return 1;
+ else if (fatal)
+ return -1;
+ else
+ return 0;
+}
+
+/**********************
+SslBox_t::GetPeerCert
+**********************/
+
+X509 *SslBox_t::GetPeerCert()
+{
+ X509 *cert = NULL;
+
+ if (pSSL)
+ cert = SSL_get_peer_certificate(pSSL);
+
+ return cert;
+}
+
+/**********************
+SslBox_t::GetCipherBits
+**********************/
+
+int SslBox_t::GetCipherBits()
+{
+ int bits = -1;
+ if (pSSL)
+ SSL_get_cipher_bits(pSSL, &bits);
+ return bits;
+}
+
+/**********************
+SslBox_t::GetCipherName
+**********************/
+
+const char *SslBox_t::GetCipherName()
+{
+ if (pSSL)
+ return SSL_get_cipher_name(pSSL);
+ return NULL;
+}
+
+/**********************
+SslBox_t::GetCipherProtocol
+**********************/
+
+const char *SslBox_t::GetCipherProtocol()
+{
+ if (pSSL)
+ return SSL_get_cipher_version(pSSL);
+ return NULL;
+}
+
+/**********************
+SslBox_t::GetSNIHostname
+**********************/
+
+const char *SslBox_t::GetSNIHostname()
+{
+ #ifdef TLSEXT_NAMETYPE_host_name
+ if (pSSL)
+ return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name);
+ #endif
+ return NULL;
+}
+
+/******************
+ssl_verify_wrapper
+*******************/
+
+extern "C" int ssl_verify_wrapper(int preverify_ok UNUSED, X509_STORE_CTX *ctx)
+{
+ uintptr_t binding;
+ X509 *cert;
+ SSL *ssl;
+ BUF_MEM *buf;
+ BIO *out;
+ int result;
+
+ cert = X509_STORE_CTX_get_current_cert(ctx);
+ ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ binding = (uintptr_t) SSL_get_ex_data(ssl, 0);
+
+ out = BIO_new(BIO_s_mem());
+ PEM_write_bio_X509(out, cert);
+ BIO_write(out, "\0", 1);
+ BIO_get_mem_ptr(out, &buf);
+
+ ConnectionDescriptor *cd = dynamic_cast <ConnectionDescriptor*> (Bindable_t::GetObject(binding));
+ result = (cd->VerifySslPeer(buf->data) == true ? 1 : 0);
+ BIO_free(out);
+
+ return result;
+}
+
+#endif // WITH_SSL
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.h b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.h
new file mode 100644
index 0000000..64ff6e1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext/ssl.h
@@ -0,0 +1,103 @@
+/*****************************************************************************
+
+$Id$
+
+File: ssl.h
+Date: 30Apr06
+
+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.
+
+*****************************************************************************/
+
+
+#ifndef __SslBox__H_
+#define __SslBox__H_
+
+
+
+
+#ifdef WITH_SSL
+
+/******************
+class SslContext_t
+******************/
+
+class SslContext_t
+{
+ public:
+ SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version);
+ virtual ~SslContext_t();
+
+ private:
+ static bool bLibraryInitialized;
+
+ private:
+ bool bIsServer;
+ SSL_CTX *pCtx;
+
+ EVP_PKEY *PrivateKey;
+ X509 *Certificate;
+
+ friend class SslBox_t;
+};
+
+
+/**************
+class SslBox_t
+**************/
+
+#define SSLBOX_INPUT_CHUNKSIZE 2019
+#define SSLBOX_OUTPUT_CHUNKSIZE 2048
+#define SSLBOX_WRITE_BUFFER_SIZE 8192 // (SSLBOX_OUTPUT_CHUNKSIZE * 4)
+
+class SslBox_t
+{
+ public:
+ SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding);
+ virtual ~SslBox_t();
+
+ int PutPlaintext (const char*, int);
+ int GetPlaintext (char*, int);
+
+ bool PutCiphertext (const char*, int);
+ bool CanGetCiphertext();
+ int GetCiphertext (char*, int);
+ bool IsHandshakeCompleted() {return bHandshakeCompleted;}
+
+ X509 *GetPeerCert();
+ int GetCipherBits();
+ const char *GetCipherName();
+ const char *GetCipherProtocol();
+ const char *GetSNIHostname();
+
+ void Shutdown();
+
+ protected:
+ SslContext_t *Context;
+
+ bool bIsServer;
+ bool bHandshakeCompleted;
+ bool bVerifyPeer;
+ bool bFailIfNoPeerCert;
+ SSL *pSSL;
+ BIO *pbioRead;
+ BIO *pbioWrite;
+
+ PageList OutboundQ;
+};
+
+extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);
+
+#endif // WITH_SSL
+
+
+#endif // __SslBox__H_
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.classpath b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.classpath
new file mode 100644
index 0000000..11672dd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry excluding="src/" kind="src" path=""/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry kind="output" path="src"/>
+</classpath>
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.project b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.project
new file mode 100644
index 0000000..c7766db
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>em_reactor</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactor.java b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactor.java
new file mode 100644
index 0000000..31642f3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactor.java
@@ -0,0 +1,613 @@
+/**
+ * $Id$
+ *
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *
+ */
+
+package com.rubyeventmachine;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.nio.*;
+import java.net.*;
+import java.util.concurrent.atomic.*;
+import java.security.*;
+
+public class EmReactor {
+ public final int EM_TIMER_FIRED = 100;
+ public final int EM_CONNECTION_READ = 101;
+ public final int EM_CONNECTION_UNBOUND = 102;
+ public final int EM_CONNECTION_ACCEPTED = 103;
+ public final int EM_CONNECTION_COMPLETED = 104;
+ public final int EM_LOOPBREAK_SIGNAL = 105;
+ public final int EM_CONNECTION_NOTIFY_READABLE = 106;
+ public final int EM_CONNECTION_NOTIFY_WRITABLE = 107;
+ public final int EM_SSL_HANDSHAKE_COMPLETED = 108;
+ public final int EM_SSL_VERIFY = 109;
+ public final int EM_PROXY_TARGET_UNBOUND = 110;
+ public final int EM_PROXY_COMPLETED = 111;
+
+ public final int EM_PROTO_SSLv2 = 2;
+ public final int EM_PROTO_SSLv3 = 4;
+ public final int EM_PROTO_TLSv1 = 8;
+ public final int EM_PROTO_TLSv1_1 = 16;
+ public final int EM_PROTO_TLSv1_2 = 32;
+
+ private Selector mySelector;
+ private TreeMap<Long, ArrayList<Long>> Timers;
+ private HashMap<Long, EventableChannel> Connections;
+ private HashMap<Long, ServerSocketChannel> Acceptors;
+ private ArrayList<Long> NewConnections;
+ private ArrayList<Long> UnboundConnections;
+ private ArrayList<EventableSocketChannel> DetachedConnections;
+
+ private boolean bRunReactor;
+ private long BindingIndex;
+ private AtomicBoolean loopBreaker;
+ private ByteBuffer myReadBuffer;
+ private int timerQuantum;
+
+ public EmReactor() {
+ Timers = new TreeMap<Long, ArrayList<Long>>();
+ Connections = new HashMap<Long, EventableChannel>();
+ Acceptors = new HashMap<Long, ServerSocketChannel>();
+ NewConnections = new ArrayList<Long>();
+ UnboundConnections = new ArrayList<Long>();
+ DetachedConnections = new ArrayList<EventableSocketChannel>();
+
+ BindingIndex = 0;
+ loopBreaker = new AtomicBoolean();
+ loopBreaker.set(false);
+ myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
+ timerQuantum = 98;
+ }
+
+ /**
+ * This is a no-op stub, intended to be overridden in user code.
+ */
+ public void eventCallback (long sig, int eventType, ByteBuffer data, long data2) {
+ System.out.println ("Default callback: "+sig+" "+eventType+" "+data+" "+data2);
+ }
+ public void eventCallback (long sig, int eventType, ByteBuffer data) {
+ eventCallback (sig, eventType, data, 0);
+ }
+
+
+ public void run() {
+ try {
+ mySelector = Selector.open();
+ bRunReactor = true;
+ } catch (IOException e) {
+ throw new RuntimeException ("Could not open selector", e);
+ }
+
+ while (bRunReactor) {
+ runLoopbreaks();
+ if (!bRunReactor) break;
+
+ runTimers();
+ if (!bRunReactor) break;
+
+ removeUnboundConnections();
+ checkIO();
+ addNewConnections();
+ processIO();
+ }
+
+ close();
+ }
+
+ void addNewConnections() {
+ ListIterator<EventableSocketChannel> iter = DetachedConnections.listIterator(0);
+ while (iter.hasNext()) {
+ EventableSocketChannel ec = iter.next();
+ ec.cleanup();
+ }
+ DetachedConnections.clear();
+
+ ListIterator<Long> iter2 = NewConnections.listIterator(0);
+ while (iter2.hasNext()) {
+ long b = iter2.next();
+
+ EventableChannel ec = Connections.get(b);
+ if (ec != null) {
+ try {
+ ec.register();
+ } catch (ClosedChannelException e) {
+ UnboundConnections.add (ec.getBinding());
+ }
+ }
+ }
+ NewConnections.clear();
+ }
+
+ void removeUnboundConnections() {
+ ListIterator<Long> iter = UnboundConnections.listIterator(0);
+ while (iter.hasNext()) {
+ long b = iter.next();
+
+ EventableChannel ec = Connections.remove(b);
+ if (ec != null) {
+ eventCallback (b, EM_CONNECTION_UNBOUND, null);
+ ec.close();
+
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
+ if (sc != null && sc.isAttached())
+ DetachedConnections.add (sc);
+ }
+ }
+ UnboundConnections.clear();
+ }
+
+ void checkIO() {
+ long timeout;
+
+ if (NewConnections.size() > 0) {
+ timeout = -1;
+ } else if (!Timers.isEmpty()) {
+ long now = new Date().getTime();
+ long k = Timers.firstKey();
+ long diff = k-now;
+
+ if (diff <= 0)
+ timeout = -1; // don't wait, just poll once
+ else
+ timeout = diff;
+ } else {
+ timeout = 0; // wait indefinitely
+ }
+
+ try {
+ if (timeout == -1)
+ mySelector.selectNow();
+ else
+ mySelector.select(timeout);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void processIO() {
+ Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
+ while (it.hasNext()) {
+ SelectionKey k = it.next();
+ it.remove();
+
+ if (k.isConnectable())
+ isConnectable(k);
+
+ else if (k.isAcceptable())
+ isAcceptable(k);
+
+ else {
+ if (k.isWritable())
+ isWritable(k);
+
+ if (k.isReadable())
+ isReadable(k);
+ }
+ }
+ }
+
+ void isAcceptable (SelectionKey k) {
+ ServerSocketChannel ss = (ServerSocketChannel) k.channel();
+ SocketChannel sn;
+ long b;
+
+ for (int n = 0; n < 10; n++) {
+ try {
+ sn = ss.accept();
+ if (sn == null)
+ break;
+ } catch (IOException e) {
+ e.printStackTrace();
+ k.cancel();
+
+ ServerSocketChannel server = Acceptors.remove(k.attachment());
+ if (server != null)
+ try{ server.close(); } catch (IOException ex) {};
+ break;
+ }
+
+ try {
+ sn.configureBlocking(false);
+ } catch (IOException e) {
+ e.printStackTrace();
+ continue;
+ }
+
+ b = createBinding();
+ EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
+ Connections.put (b, ec);
+ NewConnections.add (b);
+
+ eventCallback (((Long)k.attachment()).longValue(), EM_CONNECTION_ACCEPTED, null, b);
+ }
+ }
+
+ void isReadable (SelectionKey k) {
+ EventableChannel ec = (EventableChannel) k.attachment();
+ long b = ec.getBinding();
+
+ if (ec.isWatchOnly()) {
+ if (ec.isNotifyReadable())
+ eventCallback (b, EM_CONNECTION_NOTIFY_READABLE, null);
+ } else {
+ myReadBuffer.clear();
+
+ try {
+ ec.readInboundData (myReadBuffer);
+ myReadBuffer.flip();
+ if (myReadBuffer.limit() > 0)
+ eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
+ } catch (IOException e) {
+ UnboundConnections.add (b);
+ }
+ }
+ }
+
+ void isWritable (SelectionKey k) {
+ EventableChannel ec = (EventableChannel) k.attachment();
+ long b = ec.getBinding();
+
+ if (ec.isWatchOnly()) {
+ if (ec.isNotifyWritable())
+ eventCallback (b, EM_CONNECTION_NOTIFY_WRITABLE, null);
+ }
+ else {
+ try {
+ if (!ec.writeOutboundData())
+ UnboundConnections.add (b);
+ } catch (IOException e) {
+ UnboundConnections.add (b);
+ }
+ }
+ }
+
+ void isConnectable (SelectionKey k) {
+ EventableSocketChannel ec = (EventableSocketChannel) k.attachment();
+ long b = ec.getBinding();
+
+ try {
+ if (ec.finishConnecting())
+ eventCallback (b, EM_CONNECTION_COMPLETED, null);
+ else
+ UnboundConnections.add (b);
+ } catch (IOException e) {
+ UnboundConnections.add (b);
+ }
+ }
+
+ void close() {
+ try {
+ if (mySelector != null)
+ mySelector.close();
+ } catch (IOException e) {}
+ mySelector = null;
+
+ // run down open connections and sockets.
+ Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
+ while (i.hasNext()) {
+ try {
+ i.next().close();
+ } catch (IOException e) {}
+ }
+
+ // 29Sep09: We create an ArrayList of the existing connections, then iterate over
+ // that to call unbind on them. This is because an unbind can trigger a reconnect,
+ // which will add to the Connections HashMap, causing a ConcurrentModificationException.
+ // XXX: The correct behavior here would be to latch the various reactor methods to return
+ // immediately if the reactor is shutting down.
+ ArrayList<EventableChannel> conns = new ArrayList<EventableChannel>();
+ Iterator<EventableChannel> i2 = Connections.values().iterator();
+ while (i2.hasNext()) {
+ EventableChannel ec = i2.next();
+ if (ec != null) {
+ conns.add (ec);
+ }
+ }
+ Connections.clear();
+
+ ListIterator<EventableChannel> i3 = conns.listIterator(0);
+ while (i3.hasNext()) {
+ EventableChannel ec = i3.next();
+ eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, null);
+ ec.close();
+
+ EventableSocketChannel sc = (EventableSocketChannel) ec;
+ if (sc != null && sc.isAttached())
+ DetachedConnections.add (sc);
+ }
+
+ ListIterator<EventableSocketChannel> i4 = DetachedConnections.listIterator(0);
+ while (i4.hasNext()) {
+ EventableSocketChannel ec = i4.next();
+ ec.cleanup();
+ }
+ DetachedConnections.clear();
+ }
+
+ void runLoopbreaks() {
+ if (loopBreaker.getAndSet(false)) {
+ eventCallback (0, EM_LOOPBREAK_SIGNAL, null);
+ }
+ }
+
+ public void stop() {
+ bRunReactor = false;
+ signalLoopbreak();
+ }
+
+ void runTimers() {
+ long now = new Date().getTime();
+ while (!Timers.isEmpty()) {
+ long k = Timers.firstKey();
+ if (k > now)
+ break;
+
+ ArrayList<Long> callbacks = Timers.get(k);
+ Timers.remove(k);
+
+ // Fire all timers at this timestamp
+ ListIterator<Long> iter = callbacks.listIterator(0);
+ while (iter.hasNext()) {
+ eventCallback (0, EM_TIMER_FIRED, null, iter.next().longValue());
+ }
+ }
+ }
+
+ public long installOneshotTimer (long milliseconds) {
+ long s = createBinding();
+ long deadline = new Date().getTime() + milliseconds;
+
+ if (Timers.containsKey(deadline)) {
+ Timers.get(deadline).add(s);
+ } else {
+ ArrayList<Long> callbacks = new ArrayList<Long>();
+ callbacks.add(s);
+ Timers.put(deadline, callbacks);
+ }
+
+ return s;
+ }
+
+ public long startTcpServer (SocketAddress sa) throws EmReactorException {
+ try {
+ ServerSocketChannel server = ServerSocketChannel.open();
+ server.configureBlocking(false);
+ server.socket().bind (sa);
+ long s = createBinding();
+ Acceptors.put(s, server);
+ server.register(mySelector, SelectionKey.OP_ACCEPT, s);
+ return s;
+ } catch (IOException e) {
+ throw new EmReactorException ("unable to open socket acceptor: " + e.toString());
+ }
+ }
+
+ public long startTcpServer (String address, int port) throws EmReactorException {
+ return startTcpServer (new InetSocketAddress (address, port));
+ }
+
+ public void stopTcpServer (long signature) throws IOException {
+ ServerSocketChannel server = Acceptors.remove(signature);
+ if (server != null)
+ server.close();
+ else
+ throw new RuntimeException ("failed to close unknown acceptor");
+ }
+
+ public long openUdpSocket (InetSocketAddress address) throws IOException {
+ // TODO, don't throw an exception out of here.
+ DatagramChannel dg = DatagramChannel.open();
+ dg.configureBlocking(false);
+ dg.socket().bind(address);
+ long b = createBinding();
+ EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
+ dg.register(mySelector, SelectionKey.OP_READ, ec);
+ Connections.put(b, ec);
+ return b;
+ }
+
+ public long openUdpSocket (String address, int port) throws IOException {
+ return openUdpSocket (new InetSocketAddress (address, port));
+ }
+
+ public void sendData (long sig, ByteBuffer bb) throws IOException {
+ Connections.get(sig).scheduleOutboundData( bb );
+ }
+
+ public void sendData (long sig, byte[] data) throws IOException {
+ sendData (sig, ByteBuffer.wrap(data));
+ }
+
+ public void setCommInactivityTimeout (long sig, long mills) {
+ Connections.get(sig).setCommInactivityTimeout (mills);
+ }
+
+ public void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort) {
+ sendDatagram (sig, ByteBuffer.wrap(data), recipAddress, recipPort);
+ }
+
+ public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) {
+ (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
+ }
+
+ public long connectTcpServer (String address, int port) {
+ return connectTcpServer(null, 0, address, port);
+ }
+
+ public long connectTcpServer (String bindAddr, int bindPort, String address, int port) {
+ long b = createBinding();
+
+ try {
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ if (bindAddr != null)
+ sc.socket().bind(new InetSocketAddress (bindAddr, bindPort));
+
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
+
+ if (sc.connect (new InetSocketAddress (address, port))) {
+ // Connection returned immediately. Can happen with localhost connections.
+ // WARNING, this code is untested due to lack of available test conditions.
+ // Ought to be be able to come here from a localhost connection, but that
+ // doesn't happen on Linux. (Maybe on FreeBSD?)
+ // The reason for not handling this until we can test it is that we
+ // really need to return from this function WITHOUT triggering any EM events.
+ // That's because until the user code has seen the signature we generated here,
+ // it won't be able to properly dispatch them. The C++ EM deals with this
+ // by setting pending mode as a flag in ALL eventable descriptors and making
+ // the descriptor select for writable. Then, it can send UNBOUND and
+ // CONNECTION_COMPLETED on the next pass through the loop, because writable will
+ // fire.
+ throw new RuntimeException ("immediate-connect unimplemented");
+ }
+ else {
+ ec.setConnectPending();
+ Connections.put (b, ec);
+ NewConnections.add (b);
+ }
+ } catch (IOException e) {
+ // Can theoretically come here if a connect failure can be determined immediately.
+ // I don't know how to make that happen for testing purposes.
+ throw new RuntimeException ("immediate-connect unimplemented: " + e.toString());
+ }
+ return b;
+ }
+
+ public void closeConnection (long sig, boolean afterWriting) {
+ EventableChannel ec = Connections.get(sig);
+ if (ec != null)
+ if (ec.scheduleClose (afterWriting))
+ UnboundConnections.add (sig);
+ }
+
+ long createBinding() {
+ return ++BindingIndex;
+ }
+
+ public void signalLoopbreak() {
+ loopBreaker.set(true);
+ if (mySelector != null)
+ mySelector.wakeup();
+ }
+
+ public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException {
+ Connections.get(sig).startTls();
+ }
+
+ public void setTimerQuantum (int mills) {
+ if (mills < 5 || mills > 2500)
+ throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
+ timerQuantum = mills;
+ }
+
+ public Object[] getPeerName (long sig) {
+ EventableChannel channel = Connections.get(sig);
+ if (channel != null) {
+ return Connections.get(sig).getPeerName();
+ }
+ else {
+ ServerSocketChannel acceptor = Acceptors.get(sig);
+ return new Object[] { acceptor.socket().getLocalPort(),
+ acceptor.socket().getInetAddress().getHostAddress() };
+ }
+ }
+
+ public Object[] getSockName (long sig) {
+ EventableChannel channel = Connections.get(sig);
+ if (channel != null) {
+ return Connections.get(sig).getSockName();
+ }
+ else {
+ ServerSocketChannel acceptor = Acceptors.get(sig);
+ return new Object[] { acceptor.socket().getLocalPort(),
+ acceptor.socket().getInetAddress().getHostAddress() };
+ }
+ }
+
+ public long attachChannel (SocketChannel sc, boolean watch_mode) {
+ long b = createBinding();
+
+ EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
+
+ ec.setAttached();
+ if (watch_mode)
+ ec.setWatchOnly();
+
+ Connections.put (b, ec);
+ NewConnections.add (b);
+
+ return b;
+ }
+
+ public SocketChannel detachChannel (long sig) {
+ EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig);
+ if (ec != null) {
+ UnboundConnections.add (sig);
+ return ec.getChannel();
+ } else {
+ return null;
+ }
+ }
+
+ public void setNotifyReadable (long sig, boolean mode) {
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode);
+ }
+
+ public void setNotifyWritable (long sig, boolean mode) {
+ ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode);
+ }
+
+ public boolean isNotifyReadable (long sig) {
+ return Connections.get(sig).isNotifyReadable();
+ }
+
+ public boolean isNotifyWritable (long sig) {
+ return Connections.get(sig).isNotifyWritable();
+ }
+
+ public boolean pauseConnection (long sig) {
+ return ((EventableSocketChannel) Connections.get(sig)).pause();
+ }
+
+ public boolean resumeConnection (long sig) {
+ return ((EventableSocketChannel) Connections.get(sig)).resume();
+ }
+
+ public boolean isConnectionPaused (long sig) {
+ return ((EventableSocketChannel) Connections.get(sig)).isPaused();
+ }
+
+ public long getOutboundDataSize (long sig) {
+ return Connections.get(sig).getOutboundDataSize();
+ }
+
+ public int getConnectionCount() {
+ return Connections.size() + Acceptors.size();
+ }
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactorException.java b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactorException.java
new file mode 100644
index 0000000..69efaa3
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EmReactorException.java
@@ -0,0 +1,40 @@
+/**
+ * $Id$
+ *
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *
+ */
+
+package com.rubyeventmachine;
+
+/**
+ * @author francis
+ *
+ */
+public class EmReactorException extends Exception {
+ static final long serialVersionUID = 0;
+ public EmReactorException (String msg) {
+ super (msg);
+ }
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableChannel.java b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableChannel.java
new file mode 100644
index 0000000..3d4f5fd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableChannel.java
@@ -0,0 +1,72 @@
+/**
+ * $Id$
+ *
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *
+ */
+
+
+package com.rubyeventmachine;
+
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+
+public interface EventableChannel {
+
+ public void scheduleOutboundData (ByteBuffer bb);
+
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort);
+
+ public boolean scheduleClose (boolean afterWriting);
+
+ public void startTls();
+
+ public long getBinding();
+
+ public void readInboundData (ByteBuffer dst) throws IOException;
+
+ public void register() throws ClosedChannelException;
+
+ /**
+ * This is called by the reactor after it finishes running.
+ * The idea is to free network resources.
+ */
+ public void close();
+
+ public boolean writeOutboundData() throws IOException;
+
+ public long getOutboundDataSize();
+
+ public void setCommInactivityTimeout (long seconds);
+
+ public Object[] getPeerName();
+ public Object[] getSockName();
+
+ public boolean isWatchOnly();
+
+ public boolean isNotifyReadable();
+ public boolean isNotifyWritable();
+
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableDatagramChannel.java b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableDatagramChannel.java
new file mode 100644
index 0000000..df1c9fd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableDatagramChannel.java
@@ -0,0 +1,201 @@
+/**
+ * $Id$
+ *
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *
+ */
+
+
+package com.rubyeventmachine;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.DatagramChannel;
+import java.util.LinkedList;
+import java.io.*;
+import java.net.*;
+
+public class EventableDatagramChannel implements EventableChannel {
+
+ class Packet {
+ public ByteBuffer bb;
+ public SocketAddress recipient;
+ public Packet (ByteBuffer _bb, SocketAddress _recipient) {
+ bb = _bb;
+ recipient = _recipient;
+ }
+ }
+
+ DatagramChannel channel;
+ long binding;
+ Selector selector;
+ boolean bCloseScheduled;
+ LinkedList<Packet> outboundQ;
+ long outboundS;
+ SocketAddress returnAddress;
+
+
+ public EventableDatagramChannel (DatagramChannel dc, long _binding, Selector sel) throws ClosedChannelException {
+ channel = dc;
+ binding = _binding;
+ selector = sel;
+ bCloseScheduled = false;
+ outboundQ = new LinkedList<Packet>();
+ outboundS = 0;
+
+ dc.register(selector, SelectionKey.OP_READ, this);
+ }
+
+ public void scheduleOutboundData (ByteBuffer bb) {
+ try {
+ if ((!bCloseScheduled) && (bb.remaining() > 0)) {
+ outboundQ.addLast(new Packet(bb, returnAddress));
+ outboundS += bb.remaining();
+ channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
+ }
+ } catch (ClosedChannelException e) {
+ throw new RuntimeException ("no outbound data");
+ }
+ }
+
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
+ try {
+ if ((!bCloseScheduled) && (bb.remaining() > 0)) {
+ outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort)));
+ outboundS += bb.remaining();
+ channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
+ }
+ } catch (ClosedChannelException e) {
+ throw new RuntimeException ("no outbound data");
+ }
+ }
+
+ public boolean scheduleClose (boolean afterWriting) {
+ System.out.println ("NOT SCHEDULING CLOSE ON DATAGRAM");
+ return false;
+ }
+
+ public void startTls() {
+ throw new RuntimeException ("TLS is unimplemented on this Channel");
+ }
+
+ public long getBinding() {
+ return binding;
+ }
+
+ public void register() throws ClosedChannelException {
+ // TODO
+ }
+
+ /**
+ * Terminate with extreme prejudice. Don't assume there will be another pass through
+ * the reactor core.
+ */
+ public void close() {
+ try {
+ channel.close();
+ } catch (IOException e) {
+ }
+ }
+
+ public void readInboundData (ByteBuffer dst) {
+ returnAddress = null;
+ try {
+ // If there is no datagram available (we're nonblocking after all),
+ // then channel.receive returns null.
+ returnAddress = channel.receive(dst);
+ } catch (IOException e) {
+ // probably a no-op. The caller will see the empty (or even partial) buffer
+ // and presumably do the right thing.
+ }
+ }
+
+ public boolean writeOutboundData() {
+ while (!outboundQ.isEmpty()) {
+ Packet p = outboundQ.getFirst();
+ int written = 0;
+ try {
+ // With a datagram socket, it's ok to send an empty buffer.
+ written = channel.send(p.bb, p.recipient);
+ outboundS -= written;
+ }
+ catch (IOException e) {
+ return false;
+ }
+
+ /* Did we consume the whole outbound buffer? If yes, pop it off and
+ * keep looping. If no, the outbound network buffers are full, so break
+ * out of here. There's a flaw that affects outbound buffers that are intentionally
+ * empty. We can tell whether they got sent or not. So we assume they were.
+ * TODO: As implemented, this ALWAYS discards packets if they were at least
+ * partially written. This matches the behavior of the C++ EM. My judgment
+ * is that this is less surprising than fragmenting the data and sending multiple
+ * packets would be. I could be wrong, so this is subject to change.
+ */
+
+ if ((written > 0) || (p.bb.remaining() == 0))
+ outboundQ.removeFirst();
+ else
+ break;
+ }
+
+ if (outboundQ.isEmpty()) {
+ try {
+ channel.register(selector, SelectionKey.OP_READ, this);
+ } catch (ClosedChannelException e) {}
+ }
+
+ // ALWAYS drain the outbound queue before triggering a connection close.
+ // If anyone wants to close immediately, they're responsible for clearing
+ // the outbound queue.
+ return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
+ }
+
+ public void setCommInactivityTimeout (long seconds) {
+ // TODO
+ System.out.println ("DATAGRAM: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
+ }
+
+ public Object[] getPeerName () {
+ if (returnAddress != null) {
+ InetSocketAddress inetAddr = (InetSocketAddress) returnAddress;
+ return new Object[]{ inetAddr.getPort(), inetAddr.getHostName() };
+ } else {
+ return null;
+ }
+ }
+
+ public Object[] getSockName () {
+ DatagramSocket socket = channel.socket();
+ return new Object[]{ socket.getLocalPort(),
+ socket.getLocalAddress().getHostAddress() };
+ }
+
+ public boolean isWatchOnly() { return false; }
+ public boolean isNotifyReadable() { return false; }
+ public boolean isNotifyWritable() { return false; }
+ public long getOutboundDataSize() { return outboundS; }
+}
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableSocketChannel.java b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableSocketChannel.java
new file mode 100644
index 0000000..2905ec6
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/java/src/com/rubyeventmachine/EventableSocketChannel.java
@@ -0,0 +1,415 @@
+/**
+ * $Id$
+ *
+ * Author:: Francis Cianfrocca (gmail: blackhedd)
+ * Homepage:: http://rubyeventmachine.com
+ * Date:: 15 Jul 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *
+ */
+
+/**
+ *
+ */
+package com.rubyeventmachine;
+
+/**
+ * @author francis
+ *
+ */
+
+import java.nio.channels.*;
+import java.nio.*;
+import java.util.*;
+import java.io.*;
+import java.net.Socket;
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.lang.reflect.Field;
+
+import java.security.*;
+
+public class EventableSocketChannel implements EventableChannel {
+ Selector selector;
+ SelectionKey channelKey;
+ SocketChannel channel;
+
+ long binding;
+ LinkedList<ByteBuffer> outboundQ;
+ long outboundS;
+
+ boolean bCloseScheduled;
+ boolean bConnectPending;
+ boolean bWatchOnly;
+ boolean bAttached;
+ boolean bNotifyReadable;
+ boolean bNotifyWritable;
+ boolean bPaused;
+
+ SSLEngine sslEngine;
+ SSLContext sslContext;
+
+ public EventableSocketChannel (SocketChannel sc, long _binding, Selector sel) {
+ channel = sc;
+ binding = _binding;
+ selector = sel;
+ bCloseScheduled = false;
+ bConnectPending = false;
+ bWatchOnly = false;
+ bAttached = false;
+ bNotifyReadable = false;
+ bNotifyWritable = false;
+ outboundQ = new LinkedList<ByteBuffer>();
+ outboundS = 0;
+ }
+
+ public long getBinding() {
+ return binding;
+ }
+
+ public SocketChannel getChannel() {
+ return channel;
+ }
+
+ public void register() throws ClosedChannelException {
+ if (channelKey == null) {
+ int events = currentEvents();
+ channelKey = channel.register(selector, events, this);
+ }
+ }
+
+ /**
+ * Terminate with extreme prejudice. Don't assume there will be another pass through
+ * the reactor core.
+ */
+ public void close() {
+ if (channelKey != null) {
+ channelKey.cancel();
+ channelKey = null;
+ }
+
+ if (bAttached) {
+ // attached channels are copies, so reset the file descriptor to prevent java from close()ing it
+ Field f;
+ FileDescriptor fd;
+
+ try {
+ /* do _NOT_ clobber fdVal here, it will break epoll/kqueue on jdk6!
+ * channelKey.cancel() above does not occur until the next call to select
+ * and if fdVal is gone, we will continue to get events for this fd.
+ *
+ * instead, remove fdVal in cleanup(), which is processed via DetachedConnections,
+ * after UnboundConnections but before NewConnections.
+ */
+
+ f = channel.getClass().getDeclaredField("fd");
+ f.setAccessible(true);
+ fd = (FileDescriptor) f.get(channel);
+
+ f = fd.getClass().getDeclaredField("fd");
+ f.setAccessible(true);
+ f.set(fd, -1);
+ } catch (java.lang.NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (java.lang.IllegalAccessException e) {
+ e.printStackTrace();
+ }
+
+ return;
+ }
+
+ try {
+ channel.close();
+ } catch (IOException e) {
+ }
+ }
+
+ public void cleanup() {
+ if (bAttached) {
+ Field f;
+ try {
+ f = channel.getClass().getDeclaredField("fdVal");
+ f.setAccessible(true);
+ f.set(channel, -1);
+ } catch (java.lang.NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (java.lang.IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ channel = null;
+ }
+
+ public void scheduleOutboundData (ByteBuffer bb) {
+ if (!bCloseScheduled && bb.remaining() > 0) {
+ if (sslEngine != null) {
+ try {
+ ByteBuffer b = ByteBuffer.allocate(32*1024); // TODO, preallocate this buffer.
+ sslEngine.wrap(bb, b);
+ b.flip();
+ outboundQ.addLast(b);
+ outboundS += b.remaining();
+ } catch (SSLException e) {
+ throw new RuntimeException ("ssl error");
+ }
+ }
+ else {
+ outboundQ.addLast(bb);
+ outboundS += bb.remaining();
+ }
+
+ updateEvents();
+ }
+ }
+
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
+ throw new RuntimeException ("datagram sends not supported on this channel");
+ }
+
+ /**
+ * Called by the reactor when we have selected readable.
+ */
+ public void readInboundData (ByteBuffer bb) throws IOException {
+ if (channel.read(bb) == -1)
+ throw new IOException ("eof");
+ }
+
+ public long getOutboundDataSize() { return outboundS; }
+
+ /**
+ * Called by the reactor when we have selected writable.
+ * Return false to indicate an error that should cause the connection to close.
+ * TODO, VERY IMPORTANT: we're here because we selected writable, but it's always
+ * possible to become unwritable between the poll and when we get here. The way
+ * this code is written, we're depending on a nonblocking write NOT TO CONSUME
+ * the whole outbound buffer in this case, rather than firing an exception.
+ * We should somehow verify that this is indeed Java's defined behavior.
+ * @return
+ */
+ public boolean writeOutboundData() throws IOException {
+ ByteBuffer[] bufs = new ByteBuffer[64];
+ int i;
+ long written, toWrite;
+ while (!outboundQ.isEmpty()) {
+ i = 0;
+ toWrite = 0;
+ written = 0;
+ while (i < 64 && !outboundQ.isEmpty()) {
+ bufs[i] = outboundQ.removeFirst();
+ toWrite += bufs[i].remaining();
+ i++;
+ }
+ if (toWrite > 0)
+ written = channel.write(bufs, 0, i);
+
+ outboundS -= written;
+ // Did we consume the whole outbound buffer? If yes,
+ // pop it off and keep looping. If no, the outbound network
+ // buffers are full, so break out of here.
+ if (written < toWrite) {
+ while (i > 0 && bufs[i-1].remaining() > 0) {
+ outboundQ.addFirst(bufs[i-1]);
+ i--;
+ }
+ break;
+ }
+ }
+
+ if (outboundQ.isEmpty() && !bCloseScheduled) {
+ updateEvents();
+ }
+
+ // ALWAYS drain the outbound queue before triggering a connection close.
+ // If anyone wants to close immediately, they're responsible for clearing
+ // the outbound queue.
+ return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
+ }
+
+ public void setConnectPending() {
+ bConnectPending = true;
+ updateEvents();
+ }
+
+ /**
+ * Called by the reactor when we have selected connectable.
+ * Return false to indicate an error that should cause the connection to close.
+ */
+ public boolean finishConnecting() throws IOException {
+ channel.finishConnect();
+
+ bConnectPending = false;
+ updateEvents();
+ return true;
+ }
+
+ public boolean scheduleClose (boolean afterWriting) {
+ // TODO: What the hell happens here if bConnectPending is set?
+ if (!afterWriting) {
+ outboundQ.clear();
+ outboundS = 0;
+ }
+
+ if (outboundQ.isEmpty())
+ return true;
+ else {
+ updateEvents();
+ bCloseScheduled = true;
+ return false;
+ }
+ }
+
+ public void startTls() {
+ if (sslEngine == null) {
+ try {
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, null, null); // TODO, fill in the parameters.
+ sslEngine = sslContext.createSSLEngine(); // TODO, should use the parameterized version, to get Kerb stuff and session re-use.
+ sslEngine.setUseClientMode(false);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
+ } catch (KeyManagementException e) {
+ throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
+ }
+ }
+ System.out.println ("Starting TLS");
+ }
+
+ public ByteBuffer dispatchInboundData (ByteBuffer bb) throws SSLException {
+ if (sslEngine != null) {
+ if (true) throw new RuntimeException ("TLS currently unimplemented");
+ System.setProperty("javax.net.debug", "all");
+ ByteBuffer w = ByteBuffer.allocate(32*1024); // TODO, WRONG, preallocate this buffer.
+ SSLEngineResult res = sslEngine.unwrap(bb, w);
+ if (res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ Runnable r;
+ while ((r = sslEngine.getDelegatedTask()) != null) {
+ r.run();
+ }
+ }
+ System.out.println (bb);
+ w.flip();
+ return w;
+ }
+ else
+ return bb;
+ }
+
+ public void setCommInactivityTimeout (long seconds) {
+ // TODO
+ System.out.println ("SOCKET: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
+ }
+
+ public Object[] getPeerName () {
+ Socket sock = channel.socket();
+ return new Object[]{ sock.getPort(), sock.getInetAddress().getHostAddress() };
+ }
+
+ public Object[] getSockName () {
+ Socket sock = channel.socket();
+ return new Object[]{ sock.getLocalPort(),
+ sock.getLocalAddress().getHostAddress() };
+ }
+
+ public void setWatchOnly() {
+ bWatchOnly = true;
+ updateEvents();
+ }
+ public boolean isWatchOnly() { return bWatchOnly; }
+
+ public void setAttached() {
+ bAttached = true;
+ }
+ public boolean isAttached() { return bAttached; }
+
+ public void setNotifyReadable (boolean mode) {
+ bNotifyReadable = mode;
+ updateEvents();
+ }
+ public boolean isNotifyReadable() { return bNotifyReadable; }
+
+ public void setNotifyWritable (boolean mode) {
+ bNotifyWritable = mode;
+ updateEvents();
+ }
+ public boolean isNotifyWritable() { return bNotifyWritable; }
+
+ public boolean pause() {
+ if (bWatchOnly) {
+ throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+ }
+ boolean old = bPaused;
+ bPaused = true;
+ updateEvents();
+ return !old;
+ }
+
+ public boolean resume() {
+ if (bWatchOnly) {
+ throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
+ }
+ boolean old = bPaused;
+ bPaused = false;
+ updateEvents();
+ return old;
+ }
+
+ public boolean isPaused() {
+ return bPaused;
+ }
+
+ private void updateEvents() {
+ if (channelKey == null)
+ return;
+
+ int events = currentEvents();
+
+ if (channelKey.interestOps() != events) {
+ channelKey.interestOps(events);
+ }
+ }
+
+ private int currentEvents() {
+ int events = 0;
+
+ if (bWatchOnly)
+ {
+ if (bNotifyReadable)
+ events |= SelectionKey.OP_READ;
+
+ if (bNotifyWritable)
+ events |= SelectionKey.OP_WRITE;
+ }
+ else if (!bPaused)
+ {
+ if (bConnectPending)
+ events |= SelectionKey.OP_CONNECT;
+ else {
+ events |= SelectionKey.OP_READ;
+
+ if (!outboundQ.isEmpty())
+ events |= SelectionKey.OP_WRITE;
+ }
+ }
+
+ return events;
+ }
+}
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
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/package.rake b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/package.rake
new file mode 100644
index 0000000..00419d0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/package.rake
@@ -0,0 +1,120 @@
+require 'rubygems'
+require 'rubygems/package_task'
+
+begin
+ require 'rake/extensiontask'
+ require 'rake/javaextensiontask'
+rescue LoadError => e
+ puts <<-MSG
+rake-compiler gem seems to be missing. Please install it with
+
+ gem install rake-compiler
+
+(add sudo if necessary).
+ MSG
+end
+
+Gem::PackageTask.new(GEMSPEC) do |pkg|
+end
+
+if RUBY_PLATFORM =~ /java/
+ Rake::JavaExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext|
+ ext.ext_dir = 'java/src'
+ end
+else
+ def setup_cross_compilation(ext)
+ unless RUBY_PLATFORM =~ /mswin|mingw/
+ ext.cross_compile = true
+ ext.cross_platform = ['x86-mingw32', 'x64-mingw32']
+ end
+ end
+ def hack_cross_compilation(ext)
+ # inject 1.8/1.9 pure-ruby entry point
+ # HACK: add these dependencies to the task instead of using cross_compiling
+ if ext.cross_platform.is_a?(Array)
+ ext.cross_platform.each do |platform|
+ task = "native:#{GEMSPEC.name}:#{platform}"
+ if Rake::Task.task_defined?(task)
+ Rake::Task[task].prerequisites.unshift "lib/#{ext.name}.rb"
+ end
+ end
+ end
+ end
+
+ em = Rake::ExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext|
+ ext.ext_dir = 'ext'
+ ext.source_pattern = '*.{h,c,cpp}'
+ setup_cross_compilation(ext)
+ end
+ hack_cross_compilation em
+
+ ff = Rake::ExtensionTask.new("fastfilereaderext", GEMSPEC) do |ext|
+ ext.ext_dir = 'ext/fastfilereader'
+ ext.source_pattern = '*.{h,c,cpp}'
+ setup_cross_compilation(ext)
+ end
+ hack_cross_compilation ff
+end
+
+# Setup shim files that require 1.8 vs 1.9 extensions in win32 bin gems
+%w[ rubyeventmachine fastfilereaderext ].each do |filename|
+ file("lib/#{filename}.rb") do |t|
+ File.open(t.name, 'wb') do |f|
+ f.write <<-eoruby
+ RUBY_VERSION =~ /(\\d+.\\d+)/
+ require "\#{$1}/#{File.basename(t.name, '.rb')}"
+ eoruby
+ end
+ at_exit{ FileUtils.rm t.name if File.exist?(t.name) }
+ end
+end
+
+task :cross_cxx do
+ ENV['CROSS_COMPILING'] = 'yes'
+ require 'rake/extensioncompiler'
+ ENV['CXX'] = "#{Rake::ExtensionCompiler.mingw_host}-g++"
+end
+
+if Rake::Task.task_defined?(:cross)
+ task :cross => 'lib/rubyeventmachine.rb'
+ task :cross => 'lib/fastfilereaderext.rb'
+ task :cross => :cross_cxx
+end
+
+def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end
+def sudo(cmd)
+ if windows? || (require 'etc'; Etc.getpwuid.uid == 0)
+ sh cmd
+ else
+ sh "sudo #{cmd}"
+ end
+end
+def gem_cmd(action, name, *args)
+ rb = Gem.ruby rescue nil
+ rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']))
+ sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'"
+end
+
+Rake::Task[:clean].enhance [:clobber_package]
+
+# DevKit task following the example of Luis Lavena's test-ruby-c-extension
+task :devkit do
+ begin
+ require "devkit"
+ rescue LoadError => e
+ abort "Failed to activate RubyInstaller's DevKit required for compilation."
+ end
+end
+
+if RUBY_PLATFORM =~ /mingw|mswin/
+ Rake::Task['compile'].prerequisites.unshift 'devkit'
+end
+
+desc "Build binary gems for Windows with rake-compiler-dock"
+task 'gem:windows' do
+ require 'rake_compiler_dock'
+ RakeCompilerDock.sh <<-EOT
+ RUBY_CC_VERSION="${RUBY_CC_VERSION//1.8.7/}"
+ bundle && rake cross native gem
+ EOT
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test.rake b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test.rake
new file mode 100644
index 0000000..1185ac7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test.rake
@@ -0,0 +1,8 @@
+require 'rake/testtask'
+
+Rake::TestTask.new(:test) do |t|
+ t.libs << "tests"
+ t.libs << "lib"
+ t.pattern = 'tests/**/test_*.rb'
+ t.warning = true
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test_pure.rake b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test_pure.rake
new file mode 100644
index 0000000..5a84ded
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/rakelib/test_pure.rake
@@ -0,0 +1,13 @@
+require 'rake/testtask'
+
+Rake::TestTask.new(:test_pure) do |t|
+ t.libs << 'tests'
+ t.libs << 'lib'
+ t.test_files = Dir.glob('tests/**/test_pure*.rb') + Dir.glob('tests/**/test_ssl*.rb')
+ t.warning = true
+end
+
+task :test_em_pure_ruby do
+ ENV['EM_PURE_RUBY'] = 'true'
+ Rake::Task['test_pure'].execute
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.crt b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.crt
new file mode 100644
index 0000000..1919d97
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.crt
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFRDCCAywCAQEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCRU0xFTATBgNV
+BAgTDEV2ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQL
+EwtEZXZlbG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMB4XDTA5MDMyOTAy
+MzE0NloXDTEwMDMyOTAyMzE0NlowaDELMAkGA1UEBhMCRU0xFTATBgNVBAgTDEV2
+ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQLEwtEZXZl
+bG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMIICIjANBgkqhkiG9w0BAQEF
+AAOCAg8AMIICCgKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b
++hkrp9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cK
+OwzxCFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77l
+S8n2AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p
+67baDHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXg
+C8C8cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1N
+uzWD81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsy
+XXEZ2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+
+B69FJRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxD
+ohhAoKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgK
+iQYEnb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsC
+AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAj7J8fy1LUWoVWnrXDAC9jwJ1nI/YjoSU
+6ywke3o04+nZC5S+dPnuVy+HAwsU940CoNvP6RStI/bH6JL+NIqEFmwM3M8xIEWV
+MYVPkfvQUxxGvDnaY7vv93u+6Q77HV3qlhAQBHChyuXyO7TG3+WzsiT9AnBNtAP0
+4jClt5kCAQXLO/p0SFEZQ8Ru9SM8d1i73Z0VDVzs8jYWlBhiherSgbw1xK4wBOpJ
+43XmjZsBSrDpiAXd07Ak3UL2GjfT7eStgebL3UIe39ThE/s/+l43bh0M6WbOBvyQ
+i/rZ50kd1GvN0xnZhtv07hIJWO85FGWi7Oet8AzdUZJ17v1Md/f2vdhPVTFN9q+w
+mQ6LxjackqCvaJaQfBEbqsn2Tklxk4tZuDioiQbOElT2e6vljQVJWIfNx38Ny2LM
+aiXQPQu+4CI7meAh5gXM5nyJGbZvRPsxj89CqYzyHCYs5HBP3AsviBvn26ziOF+c
+544VmHd9HkIv8UTC29hh+R64RlgMQQQdaXFaUrFPTs/do0k8n/c2bPc0iTdfi5Q2
+gq6Vi8q6Ay5wGgTtRRbn/mWKuCFjEh94z6pF9Xr06NX0PuEOdf+Ls9vI5vz6G0w6
+0Li7devEN7EKBY+7Mcjg918yq9i5tEiMkUgT68788t3fTC+4iUQ5fDtdrHsaOlIR
+8bs/XQVNE/s=
+-----END CERTIFICATE-----
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.key b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.key
new file mode 100644
index 0000000..87a2531
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/client.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b+hkr
+p9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cKOwzx
+CFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77lS8n2
+AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p67ba
+DHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXgC8C8
+cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1NuzWD
+81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsyXXEZ
+2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+B69F
+JRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxDohhA
+oKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgKiQYE
+nb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsCAwEA
+AQKCAgB495RDRQB9x6hX3F+DviI8rDGug+h5FAiwJ0IBG2o1kNdbNVsTC5dvpEmg
+uPHaugCaEP+PMZbU34mNklKlb+7QbPbH18UGqz5so9TlmYOXz9oaKD6nAWL9nqRo
+02pCXQDR3DuxbhbgFnFTIECJ/jqXkl2toGaVp83W+6kZkHP8srkMyLASihWgosc+
+xRWAGvaAZtNz7br+eT5fxuH/SEKPOl1qAZ23kXrXm1XQfizk8MnMTptkUMYv+hfl
+TM98BASUsiTs6g+opy43HFn09naOQcqkWZO/8s6Gbvhi2lVfZqi5Ba6g3lVYJ3gU
+kGoako4N9qB7WqJz+LYjVR9C4TbkkJ9OD6ArwGAx5IIzC3XKSxCyY/pUn4YumPhY
+fjvY/km54TBtx/isS1TAgjSgDUxbzrfbkh7afOXSOniy9bWJMgNqHF61dqxWxmUg
+F5Tch9zH3qFFVkXpYzDU/R8ZV+CRouCvhn0eZYDh8IqIAwjH0VjkxjPyQtrdrMd3
+gDKMVKoY31EOMLZzv8a0prjpr15A+uw30tT336qb3fofks4pZKUJw8ru9jJVir2p
++RML6iUHCmIeceF7/N1meooSMLPJe0xgKeMb9M4Wtd/et2UNVtP8nCDG622rf2a0
+F/EudXuFgc3FB8nXRw9TCkw9xKQff38edG5xPFUEgqObbVl5YQKCAQEA5DDKGOmp
+EO5Zuf/kZfG6/AMMYwAuv1HrYTV2w/HnI3tyQ34Xkeqo+I/OqmRk68Ztxw4Kx1So
+SRavkotrlWhhDpl2+Yn1BjkHktSoOdf9gJ9z9llkLmbOkBjmupig1NUB7fq/4y2k
+MdqJXDy3uVKHJ97gxdIheMTyHiKuMJPnuT5lZtlT210Ig82P7sLQb/sgCfKVFTr0
+Z3haQ5/tBNKjq+igT4nMBWupOTD1q2GeZLIZACnmnUIhvu+3/bm0l+wiCB0DqF0T
+Wy9tlL3fqQSCqzevL7/k5Lg6tJTaP/XYePB73TsOtAXgIaoltXgRBsBUeE1eaODx
+kMT6E1PPtn7EqQKCAQEA1qImmTWGqhKICrwje40awPufFtZ/qXKVCN/V+zYsrJV1
+EnZpUDM+zfitlQCugnrQVHSpgfekI6mmVkmogO3fkNjUFTq+neg7IHOUHnqotx+3
+NMqIsyFInGstu9mfPd26fzZjUtx5wKF38LDTIJJAEJ83U3UpPBfpwKmiOGDXOa54
+2i4em/bb/hrQR6JySruZYLi0fXnGI5ZOfpkHgC/KOFkKNKAg2oh4B9qo7ACyiSNk
+yojb2mmn6g1OLPxi7wGUSrkS1HQq4an6RZ+eUO0HXVWag0QStdQ91M9IrIHgSBBG
+0e86Ar6jtD579gqsbz4ySpI/FqEI9obTC+E1/b0aIwKCAQAGz334qGCnZLXA22ZR
+tJlEFEM2YTcD9snzqMjWqE2hvXl3kjfZ3wsUABbG9yAb+VwlaMHhmSE8rTSoRwj6
++JaM/P+UCw4JFYKoWzh6IXwrbpbjb1+SEvdvTY71WsDSGVlpZOZ9PUt9QWyAGD/T
+hCcMhZZn0RG2rQoc5CQWxxNPcBFOtIXQMkKizGvTUHUwImqeYWMZsxzASdNH2WoV
+jsPbyaGfPhmcv83ZKyDp8IvtrXMZkiaT4vlm3Xi8VeKR9jY9z7/gMob1XcEDg3c9
+cCkGOy87WZrXSLhX02mAJzJCycqom66gqNw7pPxjIiY/8VWUEZsTvkL3cymTkhjM
+9ZOhAoIBAGUaNqJe01NTrV+ZJgGyAxM6s8LXQYV5IvjuL2bJKxwUvvP2cT9FFGWD
+qYiRrKJr5ayS07IUC+58oIzu33/0DSa27JgfduD9HrT3nKMK1mSEfRFSAjiXChQc
+bIubRGapBoub/AdxMazqoovvT1R9b84kobQfcVAMV6DYh0CVZWyXYfgsV2DSVOiK
+iufjfoDzg5lLCEI+1XW3/LunrB/W4yPN1X/amf8234ublYyt+2ucD4NUGnP05xLa
+N6P7M0MwdEEKkvMe0YBBSFH5kWK/dIOjqkgBDes20fVnuuz/tL1dZW7IiIP4dzaV
+ZGEOwBEatCfqYetv6b/u3IUxDfS7Wg8CggEBALoOwkn5LGdQg+bpdZAKJspGnJWL
+Kyr9Al2tvgc69rxfpZqS5eDLkYYCzWPpspSt0Axm1O7xOUDQDt42luaLNGJzHZ2Q
+Hn0ZNMhyHpe8d8mIQngRjD+nuLI/uFUglPzabDOCOln2aycjg1mA6ecXP1XMEVbu
+0RB/0IE36XTMfZ+u9+TRjkBLpmUaX1FdIQQWfwUou/LfaXotoQlhSGAcprLrncuJ
+T44UATYEgO/q9pMM33bdE3eBYZHoT9mSvqoLCN4s0LuwOYItIxLKUj0GulL0VQOI
+SZi+0A1c8cVDXgApkBrWPDQIR9JS4de0gW4hnDoUvHtUc2TYPRnz6N9MtFY=
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/dhparam.pem b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/dhparam.pem
new file mode 100644
index 0000000..b6464ab
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/dhparam.pem
@@ -0,0 +1,13 @@
+-----BEGIN DH PARAMETERS-----
+MIICCAKCAgEAikiatXa5aAteOtd6hOO33npjCvJByD3dwuM8rWzz0DFZdUH9nFJi
+b0VvTVweVECb6XZBsrDNLqGQykCrm43swSk5D9XQCGJLxFERD6yk3b90xaeCm3/a
+b0Ek5ZVvV73Cc/YbVmpBiOHoTFpUFJLZ7pLMQUSn8y3qUlNcY9/88HuwFi1s1lRM
+ovihSRyZMYAuYWOD4yuOuIcroKVjD6gWFrsW9XrALWny6vUXQrhk8Q3rj+wM6ZtE
+5afcB0b6ZJtphrDfk3dFjOVG/zVT37VWgrY8GABrpo2ey0W0WIQJ7rDKLaPaI4kc
+voOgC2K8Z3kSARZK+jULnwmBeYECz4EH/FF6FEp3GOKtkL4mqEkvh1n5EAesDOGl
+iiX+RZXcUrZliSeifSXBTMJWWFVC0fkGIMb9PTZfZHyAC54lpuxzVki0HIyQG9Fs
+41zBJ5e8eEoXXlfUYtduUC35YGy2IxSzYLAJE76rctAZSWghha9xLOCDFoLjMr8h
+FosKeHKJcBQ0bc8ymOpRIfrYLWhc0Pz2zkpJ/4eYw9t7NYg7S+jP19IE0gUnuM9v
+SpoYMtS28tP9nEdokdwuBKD0D3bJEBBefDlHgfXoMgvy9Hivc9PBGGNTNpyFPpwF
+sWVAkfhoNMJMC5V7LZsze+lftiDtzVoLSPDa9bO4BK7b/MgwCxfOhGsCAQI=
+-----END DH PARAMETERS-----
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/em_test_helper.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/em_test_helper.rb
new file mode 100644
index 0000000..20a3e59
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/em_test_helper.rb
@@ -0,0 +1,154 @@
+require 'em/pure_ruby' if ENV['EM_PURE_RUBY']
+require 'eventmachine'
+require 'test/unit'
+require 'rbconfig'
+require 'socket'
+
+puts "EM Library Type: #{EM.library_type}"
+
+class Test::Unit::TestCase
+ class EMTestTimeout < StandardError ; end
+
+ def setup_timeout(timeout = TIMEOUT_INTERVAL)
+ EM.schedule {
+ EM.add_timer(timeout) {
+ raise EMTestTimeout, "Test was cancelled after #{timeout} seconds."
+ }
+ }
+ end
+
+ def port_in_use?(port, host="127.0.0.1")
+ s = TCPSocket.new(host, port)
+ s.close
+ s
+ rescue Errno::ECONNREFUSED
+ false
+ end
+
+ def next_port
+ @@port ||= 9000
+ begin
+ @@port += 1
+ end while port_in_use?(@@port)
+
+ @@port
+ end
+
+ # Returns true if the host have a localhost 127.0.0.1 IPv4.
+ def self.local_ipv4?
+ return @@has_local_ipv4 if defined?(@@has_local_ipv4)
+ begin
+ get_my_ipv4_address "127.0.0.1"
+ @@has_local_ipv4 = true
+ rescue
+ @@has_local_ipv4 = false
+ end
+ end
+
+ # Returns true if the host have a public IPv4 and stores it in
+ # @@public_ipv4.
+ def self.public_ipv4?
+ return @@has_public_ipv4 if defined?(@@has_public_ipv4)
+ begin
+ @@public_ipv4 = get_my_ipv4_address "1.2.3.4"
+ @@has_public_ipv4 = true
+ rescue
+ @@has_public_ipv4 = false
+ end
+ end
+
+ # Returns true if the host have a localhost ::1 IPv6.
+ def self.local_ipv6?
+ return @@has_local_ipv6 if defined?(@@has_local_ipv6)
+ begin
+ get_my_ipv6_address "::1"
+ @@has_local_ipv6 = true
+ rescue
+ @@has_local_ipv6 = false
+ end
+ end
+
+ # Returns true if the host have a public IPv6 and stores it in
+ # @@public_ipv6.
+ def self.public_ipv6?
+ return @@has_public_ipv6 if defined?(@@has_public_ipv6)
+ begin
+ @@public_ipv6 = get_my_ipv6_address "2001::1"
+ @@has_public_ipv6 = true
+ rescue
+ @@has_public_ipv6 = false
+ end
+ end
+
+ # Returns an array with the localhost addresses (IPv4 and/or IPv6).
+ def local_ips
+ return @@local_ips if defined?(@@local_ips)
+ @@local_ips = []
+ @@local_ips << "127.0.0.1" if self.class.local_ipv4?
+ @@local_ips << "::1" if self.class.local_ipv6?
+ @@local_ips
+ end
+
+ def exception_class
+ jruby? ? NativeException : RuntimeError
+ end
+
+ module PlatformHelper
+ # http://blog.emptyway.com/2009/11/03/proper-way-to-detect-windows-platform-in-ruby/
+ def windows?
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
+ end
+
+ def solaris?
+ RUBY_PLATFORM =~ /solaris/
+ end
+
+ # http://stackoverflow.com/questions/1342535/how-can-i-tell-if-im-running-from-jruby-vs-ruby/1685970#1685970
+ def jruby?
+ defined? JRUBY_VERSION
+ end
+
+ def rbx?
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
+ end
+ end
+
+ include PlatformHelper
+ extend PlatformHelper
+
+ # Tests run significantly slower on windows. YMMV
+ TIMEOUT_INTERVAL = windows? ? 1 : 0.25
+
+ def silent
+ backup, $VERBOSE = $VERBOSE, nil
+ begin
+ yield
+ ensure
+ $VERBOSE = backup
+ end
+ end
+
+
+ private
+
+ def self.get_my_ipv4_address ip
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
+ UDPSocket.open(Socket::AF_INET) do |s|
+ s.connect ip, 1
+ s.addr.last
+ end
+ ensure
+ Socket.do_not_reverse_lookup = orig
+ end
+
+ def self.get_my_ipv6_address ip
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
+ UDPSocket.open(Socket::AF_INET6) do |s|
+ s.connect ip, 1
+ s.addr.last
+ end
+ ensure
+ Socket.do_not_reverse_lookup = orig
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_attach.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_attach.rb
new file mode 100644
index 0000000..4a55017
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_attach.rb
@@ -0,0 +1,151 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestAttach < Test::Unit::TestCase
+ class EchoServer < EM::Connection
+ def receive_data data
+ $received_data << data
+ send_data data
+ end
+ end
+
+ class EchoClient < EM::Connection
+ def initialize socket
+ self.notify_readable = true
+ @socket = socket
+ @socket.write("abc\n")
+ end
+
+ def notify_readable
+ $read = @socket.readline
+ $fd = detach
+ end
+
+ def unbind
+ EM.next_tick do
+ @socket.write("def\n")
+ EM.add_timer(0.1) { EM.stop }
+ end
+ end
+ end
+
+ def setup
+ @port = next_port
+ $read, $r, $w, $fd = nil
+ $received_data = ""
+ end
+
+ def teardown
+ [$r, $w].each do |io|
+ io.close rescue nil
+ end
+ $received_data = nil
+ end
+
+ def test_attach
+ socket = nil
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, EchoServer
+ socket = TCPSocket.new "127.0.0.1", @port
+ EM.watch socket, EchoClient, socket
+ }
+
+ assert_equal $read, "abc\n"
+ unless jruby? # jruby filenos are not real
+ assert_equal $fd, socket.fileno
+ end
+ assert_equal false, socket.closed?
+ assert_equal socket.readline, "def\n"
+ end
+
+ module PipeWatch
+ def notify_readable
+ $read = $r.readline
+ EM.stop
+ end
+ end
+
+ def test_attach_server
+ omit_if(jruby?)
+ $before = TCPServer.new("127.0.0.1", @port)
+ sig = nil
+ EM.run {
+ sig = EM.attach_server $before, EchoServer
+
+ handler = Class.new(EM::Connection) do
+ def initialize
+ send_data "hello world"
+ close_connection_after_writing
+ EM.add_timer(0.1) { EM.stop }
+ end
+ end
+ EM.connect("127.0.0.1", @port, handler)
+ }
+
+ assert_equal false, $before.closed?
+ assert_equal "hello world", $received_data
+ assert sig.is_a?(Integer)
+ end
+
+ def test_attach_pipe
+ EM.run{
+ $r, $w = IO.pipe
+ EM.watch $r, PipeWatch do |c|
+ c.notify_readable = true
+ end
+ $w.write("ghi\n")
+ }
+
+ assert_equal $read, "ghi\n"
+ end
+
+ def test_set_readable
+ before, after = nil
+
+ EM.run{
+ $r, $w = IO.pipe
+ c = EM.watch $r, PipeWatch do |con|
+ con.notify_readable = false
+ end
+
+ EM.next_tick{
+ before = c.notify_readable?
+ c.notify_readable = true
+ after = c.notify_readable?
+ }
+
+ $w.write("jkl\n")
+ }
+
+ assert !before
+ assert after
+ assert_equal $read, "jkl\n"
+ end
+
+ def test_read_write_pipe
+ result = nil
+
+ pipe_reader = Module.new do
+ define_method :receive_data do |data|
+ result = data
+ EM.stop
+ end
+ end
+
+ r,w = IO.pipe
+
+ EM.run {
+ EM.attach r, pipe_reader
+ writer = EM.attach(w)
+ writer.send_data 'ghi'
+
+ # XXX: Process will hang in Windows without this line
+ writer.close_connection_after_writing
+ }
+
+ assert_equal "ghi", result
+ ensure
+ [r,w].each {|io| io.close rescue nil }
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_basic.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_basic.rb
new file mode 100644
index 0000000..4d72524
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_basic.rb
@@ -0,0 +1,321 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestBasic < Test::Unit::TestCase
+ def setup
+ @port = next_port
+ end
+
+ def test_connection_class_cache
+ mod = Module.new
+ a, b = nil, nil
+ EM.run {
+ EM.start_server '127.0.0.1', @port, mod
+ a = EM.connect '127.0.0.1', @port, mod
+ b = EM.connect '127.0.0.1', @port, mod
+ EM.stop
+ }
+ assert_equal a.class, b.class
+ assert_kind_of EM::Connection, a
+ end
+
+ #-------------------------------------
+
+
+ def test_em
+ assert_nothing_raised do
+ EM.run {
+ setup_timeout
+ EM.add_timer 0 do
+ EM.stop
+ end
+ }
+ end
+ end
+
+ #-------------------------------------
+
+ def test_timer
+ assert_nothing_raised do
+ EM.run {
+ setup_timeout
+ n = 0
+ EM.add_periodic_timer(0.1) {
+ n += 1
+ EM.stop if n == 2
+ }
+ }
+ end
+ end
+
+ #-------------------------------------
+
+ # This test once threw an already-running exception.
+ module Trivial
+ def post_init
+ EM.stop
+ end
+ end
+
+ def test_server
+ assert_nothing_raised do
+ EM.run {
+ setup_timeout
+ EM.start_server "127.0.0.1", @port, Trivial
+ EM.connect "127.0.0.1", @port
+ }
+ end
+ end
+
+ #--------------------------------------
+
+ # EM#run_block starts the reactor loop, runs the supplied block, and then STOPS
+ # the loop automatically. Contrast with EM#run, which keeps running the reactor
+ # even after the supplied block completes.
+ def test_run_block
+ assert !EM.reactor_running?
+ a = nil
+ EM.run_block { a = "Worked" }
+ assert a
+ assert !EM.reactor_running?
+ end
+
+ class UnbindError < EM::Connection
+ ERR = Class.new(StandardError)
+ def initialize *args
+ super
+ end
+ def connection_completed
+ close_connection_after_writing
+ end
+ def unbind
+ raise ERR
+ end
+ end
+
+ def test_unbind_error_during_stop
+ assert_raises( UnbindError::ERR ) {
+ EM.run {
+ EM.start_server "127.0.0.1", @port
+ EM.connect "127.0.0.1", @port, UnbindError do
+ EM.stop
+ end
+ }
+ }
+ end
+
+ def test_unbind_error
+ EM.run {
+ EM.error_handler do |e|
+ assert(e.is_a?(UnbindError::ERR))
+ EM.stop
+ end
+ EM.start_server "127.0.0.1", @port
+ EM.connect "127.0.0.1", @port, UnbindError
+ }
+
+ # Remove the error handler before the next test
+ EM.error_handler(nil)
+ end
+
+ module BrsTestSrv
+ def receive_data data
+ $received << data
+ end
+ def unbind
+ EM.stop
+ end
+ end
+ module BrsTestCli
+ def post_init
+ send_data $sent
+ close_connection_after_writing
+ end
+ end
+
+ # From ticket #50
+ def test_byte_range_send
+ $received = ''
+ $sent = (0..255).to_a.pack('C*')
+ EM::run {
+ EM::start_server "127.0.0.1", @port, BrsTestSrv
+ EM::connect "127.0.0.1", @port, BrsTestCli
+
+ setup_timeout
+ }
+ assert_equal($sent, $received)
+ end
+
+ def test_bind_connect
+ pend('FIXME: this test is broken on Windows') if windows?
+
+ local_ip = UDPSocket.open {|s| s.connect('localhost', 80); s.addr.last }
+
+ bind_port = next_port
+
+ port, ip = nil
+ bound_server = Module.new do
+ define_method :post_init do
+ begin
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
+ ensure
+ EM.stop
+ end
+ end
+ end
+
+ EM.run do
+ setup_timeout
+ EM.start_server "127.0.0.1", @port, bound_server
+ EM.bind_connect local_ip, bind_port, "127.0.0.1", @port
+ end
+
+ assert_equal bind_port, port
+ assert_equal local_ip, ip
+ end
+
+ def test_invalid_address_bind_connect_dst
+ e = nil
+ EM.run do
+ begin
+ EM.bind_connect('localhost', nil, 'invalid.invalid', 80)
+ rescue Exception => e
+ # capture the exception
+ ensure
+ EM.stop
+ end
+ end
+
+ assert_kind_of(EventMachine::ConnectionError, e)
+ assert_match(/unable to resolve address:.*not known/, e.message)
+ end
+
+ def test_invalid_address_bind_connect_src
+ e = nil
+ EM.run do
+ begin
+ EM.bind_connect('invalid.invalid', nil, 'localhost', 80)
+ rescue Exception => e
+ # capture the exception
+ ensure
+ EM.stop
+ end
+ end
+
+ assert_kind_of(EventMachine::ConnectionError, e)
+ assert_match(/invalid bind address:.*not known/, e.message)
+ end
+
+ def test_reactor_thread?
+ assert !EM.reactor_thread?
+ EM.run { assert EM.reactor_thread?; EM.stop }
+ assert !EM.reactor_thread?
+ end
+
+ def test_schedule_on_reactor_thread
+ x = false
+ EM.run do
+ EM.schedule { x = true }
+ EM.stop
+ end
+ assert x
+ end
+
+ def test_schedule_from_thread
+ x = false
+ EM.run do
+ Thread.new { EM.schedule { x = true; EM.stop } }.join
+ end
+ assert x
+ end
+
+ def test_set_heartbeat_interval
+ omit_if(jruby?)
+ interval = 0.5
+ EM.run {
+ EM.set_heartbeat_interval interval
+ $interval = EM.get_heartbeat_interval
+ EM.stop
+ }
+ assert_equal(interval, $interval)
+ end
+
+ module PostInitRaiser
+ ERR = Class.new(StandardError)
+ def post_init
+ raise ERR
+ end
+ end
+
+ def test_bubble_errors_from_post_init
+ assert_raises(PostInitRaiser::ERR) do
+ EM.run do
+ EM.start_server "127.0.0.1", @port
+ EM.connect "127.0.0.1", @port, PostInitRaiser
+ end
+ end
+ end
+
+ module InitializeRaiser
+ ERR = Class.new(StandardError)
+ def initialize
+ raise ERR
+ end
+ end
+
+ def test_bubble_errors_from_initialize
+ assert_raises(InitializeRaiser::ERR) do
+ EM.run do
+ EM.start_server "127.0.0.1", @port
+ EM.connect "127.0.0.1", @port, InitializeRaiser
+ end
+ end
+ end
+
+ def test_schedule_close
+ omit_if(jruby?)
+ localhost, port = '127.0.0.1', 9000
+ timer_ran = false
+ num_close_scheduled = nil
+ EM.run do
+ assert_equal 0, EM.num_close_scheduled
+ EM.add_timer(1) { timer_ran = true; EM.stop }
+ EM.start_server localhost, port do |s|
+ s.close_connection
+ num_close_scheduled = EM.num_close_scheduled
+ end
+ EM.connect localhost, port do |c|
+ def c.unbind
+ EM.stop
+ end
+ end
+ end
+ assert !timer_ran
+ assert_equal 1, num_close_scheduled
+ end
+
+ def test_error_handler_idempotent # issue 185
+ errors = []
+ ticks = []
+ EM.error_handler do |e|
+ errors << e
+ end
+
+ EM.run do
+ EM.next_tick do
+ ticks << :first
+ raise
+ end
+ EM.next_tick do
+ ticks << :second
+ end
+ EM.add_timer(0.001) { EM.stop }
+ end
+
+ # Remove the error handler before the next test
+ EM.error_handler(nil)
+
+ assert_equal 1, errors.size
+ assert_equal [:first, :second], ticks
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_channel.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_channel.rb
new file mode 100644
index 0000000..c54bf1d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_channel.rb
@@ -0,0 +1,75 @@
+require 'em_test_helper'
+
+class TestEMChannel < Test::Unit::TestCase
+ def test_channel_subscribe
+ s = 0
+ EM.run do
+ c = EM::Channel.new
+ c.subscribe { |v| s = v; EM.stop }
+ c << 1
+ end
+ assert_equal 1, s
+ end
+
+ def test_channel_unsubscribe
+ s = 0
+ EM.run do
+ c = EM::Channel.new
+ subscription = c.subscribe { |v| s = v }
+ c.unsubscribe(subscription)
+ c << 1
+ EM.next_tick { EM.stop }
+ end
+ assert_not_equal 1, s
+ end
+
+ def test_channel_pop
+ s = 0
+ EM.run do
+ c = EM::Channel.new
+ c.pop{ |v| s = v }
+ c.push(1,2,3)
+ c << 4
+ c << 5
+ EM.next_tick { EM.stop }
+ end
+ assert_equal 1, s
+ end
+
+ def test_channel_reactor_thread_push
+ out = []
+ c = EM::Channel.new
+ c.subscribe { |v| out << v }
+ Thread.new { c.push(1,2,3) }.join
+ assert out.empty?
+
+ EM.run { EM.next_tick { EM.stop } }
+
+ assert_equal [1,2,3], out
+ end
+
+ def test_channel_reactor_thread_callback
+ out = []
+ c = EM::Channel.new
+ Thread.new { c.subscribe { |v| out << v } }.join
+ c.push(1,2,3)
+ assert out.empty?
+
+ EM.run { EM.next_tick { EM.stop } }
+
+ assert_equal [1,2,3], out
+ end
+
+ def test_channel_num_subscribers
+ subs = 0
+ EM.run do
+ c = EM::Channel.new
+ c.subscribe { |v| s = v }
+ c.subscribe { |v| s = v }
+ EM.next_tick { EM.stop }
+ subs = c.num_subscribers
+ end
+
+ assert_equal subs, 2
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_completion.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_completion.rb
new file mode 100644
index 0000000..1bd9a8f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_completion.rb
@@ -0,0 +1,178 @@
+require 'em_test_helper'
+require 'em/completion'
+
+class TestCompletion < Test::Unit::TestCase
+ def completion
+ @completion ||= EM::Completion.new
+ end
+
+ def crank
+ # This is a slow solution, but this just executes the next tick queue
+ # once. It's the easiest way for now.
+ EM.run { EM.stop }
+ end
+
+ def results
+ @results ||= []
+ end
+
+ def test_state
+ assert_equal :unknown, completion.state
+ end
+
+ def test_succeed
+ completion.callback { |val| results << val }
+ completion.succeed :object
+ crank
+ assert_equal :succeeded, completion.state
+ assert_equal [:object], results
+ end
+
+ def test_fail
+ completion.errback { |val| results << val }
+ completion.fail :object
+ crank
+ assert_equal :failed, completion.state
+ assert_equal [:object], results
+ end
+
+ def test_callback
+ completion.callback { results << :callback }
+ completion.errback { results << :errback }
+ completion.succeed
+ crank
+ assert_equal [:callback], results
+ end
+
+ def test_errback
+ completion.callback { results << :callback }
+ completion.errback { results << :errback }
+ completion.fail
+ crank
+ assert_equal [:errback], results
+ end
+
+ def test_stateback
+ completion.stateback(:magic) { results << :stateback }
+ completion.change_state(:magic)
+ crank
+ assert_equal [:stateback], results
+ end
+
+ def test_does_not_enqueue_when_completed
+ completion.callback { results << :callback }
+ completion.succeed
+ completion.errback { results << :errback }
+ completion.fail
+ crank
+ assert_equal [:callback], results
+ end
+
+ def test_completed
+ assert_equal false, completion.completed?
+ completion.succeed
+ assert_equal true, completion.completed?
+ completion.fail
+ assert_equal true, completion.completed?
+ completion.change_state :magic
+ assert_equal false, completion.completed?
+ end
+
+ def test_recursive_callbacks
+ completion.callback do |val|
+ results << val
+ completion.succeed :two
+ end
+ completion.callback do |val|
+ results << val
+ completion.succeed :three
+ end
+ completion.callback do |val|
+ results << val
+ end
+ completion.succeed :one
+ crank
+ assert_equal [:one, :two, :three], results
+ end
+
+ def test_late_defined_callbacks
+ completion.callback { results << :one }
+ completion.succeed
+ crank
+ assert_equal [:one], results
+ completion.callback { results << :two }
+ crank
+ assert_equal [:one, :two], results
+ end
+
+ def test_cleared_completions
+ completion.callback { results << :callback }
+ completion.errback { results << :errback }
+
+ completion.succeed
+ crank
+ completion.fail
+ crank
+ completion.succeed
+ crank
+
+ assert_equal [:callback], results
+ end
+
+ def test_skip_completed_callbacks
+ completion.callback { results << :callback }
+ completion.succeed
+ crank
+
+ completion.errback { results << :errback }
+ completion.fail
+ crank
+
+ assert_equal [:callback], results
+ end
+
+ def test_completions
+ completion.completion { results << :completion }
+ completion.succeed
+ crank
+ assert_equal [:completion], results
+
+ completion.change_state(:unknown)
+ results.clear
+
+ completion.completion { results << :completion }
+ completion.fail
+ crank
+ assert_equal [:completion], results
+ end
+
+ def test_latent_completion
+ completion.completion { results << :completion }
+ completion.succeed
+ crank
+ completion.completion { results << :completion }
+ crank
+ assert_equal [:completion, :completion], results
+ end
+
+ def test_timeout
+ args = [1, 2, 3]
+ EM.run do
+ completion.timeout(0.0001, *args)
+ completion.errback { |*errargs| results << errargs }
+ completion.completion { EM.stop }
+ EM.add_timer(0.1) { flunk 'test timed out' }
+ end
+ assert_equal [[1,2,3]], results
+ end
+
+ def test_timeout_gets_cancelled
+ EM.run do
+ completion.timeout(0.0001, :timeout)
+ completion.errback { results << :errback }
+ completion.succeed
+ EM.add_timer(0.0002) { EM.stop }
+ end
+ assert_equal [], results
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_count.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_count.rb
new file mode 100644
index 0000000..350c417
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_count.rb
@@ -0,0 +1,83 @@
+require 'em_test_helper'
+
+class TestConnectionCount < Test::Unit::TestCase
+ def teardown
+ EM.epoll = false
+ EM.kqueue = false
+ end
+
+ def test_idle_connection_count
+ count = nil
+ EM.run {
+ count = EM.connection_count
+ EM.stop_event_loop
+ }
+ assert_equal(0, count)
+ end
+
+ # Run this again with epoll enabled (if available)
+ def test_idle_connection_count_epoll
+ EM.epoll if EM.epoll?
+
+ count = nil
+ EM.run {
+ count = EM.connection_count
+ EM.stop_event_loop
+ }
+ assert_equal(0, count)
+ end
+
+ # Run this again with kqueue enabled (if available)
+ def test_idle_connection_count_kqueue
+ EM.kqueue if EM.kqueue?
+
+ count = nil
+ EM.run {
+ count = EM.connection_count
+ EM.stop_event_loop
+ }
+ assert_equal(0, count)
+ end
+
+ module Client
+ def connection_completed
+ $client_conns += 1
+ EM.stop if $client_conns == 3
+ end
+ end
+
+ def test_with_some_connections
+ EM.run {
+ $client_conns = 0
+ $initial_conns = EM.connection_count
+ EM.start_server("127.0.0.1", 9999)
+ $server_conns = EM.connection_count
+ 3.times { EM.connect("127.0.0.1", 9999, Client) }
+ }
+
+ assert_equal(0, $initial_conns)
+ assert_equal(1, $server_conns)
+ assert_equal(4, $client_conns + $server_conns)
+ end
+
+ module DoubleCloseClient
+ def unbind
+ close_connection
+ $num_close_scheduled_1 = EM.num_close_scheduled
+ EM.next_tick do
+ $num_close_scheduled_2 = EM.num_close_scheduled
+ EM.stop
+ end
+ end
+ end
+
+ def test_num_close_scheduled
+ omit_if(jruby?)
+ EM.run {
+ assert_equal(0, EM.num_close_scheduled)
+ EM.connect("127.0.0.1", 9999, DoubleCloseClient) # nothing listening on 9999
+ }
+ assert_equal(1, $num_close_scheduled_1)
+ assert_equal(0, $num_close_scheduled_2)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_write.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_write.rb
new file mode 100644
index 0000000..35533b5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_connection_write.rb
@@ -0,0 +1,35 @@
+require 'em_test_helper'
+
+class TestConnectionWrite < Test::Unit::TestCase
+
+ # This test takes advantage of the fact that EM::_RunSelectOnce iterates over the connections twice:
+ # - once to determine which ones to call Write() on
+ # - and once to call Write() on each of them.
+ #
+ # But state may change in the meantime before Write() is finally called.
+ # And that is what we try to exploit to get Write() to be called when bWatchOnly is true, and bNotifyWritable is false,
+ # to cause an assertion failure.
+
+ module SimpleClient
+ def notify_writable
+ $conn2.notify_writable = false # Being naughty in callback
+ # If this doesn't crash anything, the test passed!
+ end
+ end
+
+ def test_with_naughty_callback
+ EM.run do
+ r1, _ = IO.pipe
+ r2, _ = IO.pipe
+
+ # Adding EM.watches
+ $conn1 = EM.watch(r1, SimpleClient)
+ $conn2 = EM.watch(r2, SimpleClient)
+
+ $conn1.notify_writable = true
+ $conn2.notify_writable = true
+
+ EM.stop
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_defer.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_defer.rb
new file mode 100644
index 0000000..aeca127
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_defer.rb
@@ -0,0 +1,35 @@
+require 'em_test_helper'
+
+class TestDefer < Test::Unit::TestCase
+
+ def test_defers
+ n = 0
+ n_times = 20
+ EM.run {
+ n_times.times {
+ work_proc = proc { n += 1 }
+ callback = proc { EM.stop if n == n_times }
+ EM.defer work_proc, callback
+ }
+ }
+ assert_equal( n, n_times )
+ end
+
+ def test_errbacks
+ iterations = 20
+ callback_parameter = rand(100)
+ callback_parameters = []
+ callback_op = proc { callback_parameter }
+ callback = proc { |result| callback_parameters << result }
+ errback_parameter = Exception.new
+ errback_parameters = []
+ errback_op = proc { raise errback_parameter }
+ errback = proc { |error| errback_parameters << error }
+ EventMachine.run do
+ (1..iterations).each { |index| EventMachine.defer(index.even? ? callback_op : errback_op, callback, errback) }
+ EventMachine.add_periodic_timer(0.1) { EventMachine.stop if EventMachine.defers_finished? }
+ end
+ assert_equal(callback_parameters.select { |parameter| parameter == callback_parameter }.length, iterations * 0.5)
+ assert_equal(errback_parameters.select{ |parameter| parameter == errback_parameter }.length, iterations * 0.5)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_deferrable.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_deferrable.rb
new file mode 100644
index 0000000..5f286a7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_deferrable.rb
@@ -0,0 +1,35 @@
+require 'em_test_helper'
+
+class TestDeferrable < Test::Unit::TestCase
+ class Later
+ include EM::Deferrable
+ end
+
+ def test_timeout_without_args
+ assert_nothing_raised do
+ EM.run {
+ df = Later.new
+ df.timeout(0)
+ df.errback { EM.stop }
+ EM.add_timer(0.01) { flunk "Deferrable was not timed out." }
+ }
+ end
+ end
+
+ def test_timeout_with_args
+ args = nil
+
+ EM.run {
+ df = Later.new
+ df.timeout(0, :timeout, :foo)
+ df.errback do |type, name|
+ args = [type, name]
+ EM.stop
+ end
+
+ EM.add_timer(0.01) { flunk "Deferrable was not timed out." }
+ }
+
+ assert_equal [:timeout, :foo], args
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_epoll.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_epoll.rb
new file mode 100644
index 0000000..36f5609
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_epoll.rb
@@ -0,0 +1,142 @@
+require 'em_test_helper'
+
+
+class TestEpoll < Test::Unit::TestCase
+
+ module TestEchoServer
+ def receive_data data
+ send_data data
+ close_connection_after_writing
+ end
+ end
+
+ module TestEchoClient
+ def connection_completed
+ send_data "ABCDE"
+ $max += 1
+ end
+ def receive_data data
+ raise "bad response" unless data == "ABCDE"
+ end
+ def unbind
+ $n -= 1
+ EM.stop if $n == 0
+ end
+ end
+
+
+ # We can set the rlimit/nofile of a process but we can only set it
+ # higher if we're running as root.
+ # On most systems, the default value is 1024.
+ def test_rlimit
+ omit_if(windows? || jruby?)
+ unless EM.set_descriptor_table_size >= 1024
+ a = EM.set_descriptor_table_size
+ assert( a <= 1024 )
+ a = EM.set_descriptor_table_size( 1024 )
+ assert( a == 1024 )
+ end
+ end
+
+ # Run a high-volume version of this test by kicking the number of connections
+ # up past 512. (Each connection uses two sockets, a client and a server.)
+ # (Will require running the test as root)
+ # This test exercises TCP clients and servers.
+ #
+ # XXX this test causes all sort of weird issues on OSX (when run as part of the suite)
+ def _test_descriptors
+ EM.epoll
+ EM.set_descriptor_table_size 60000
+ EM.run {
+ EM.start_server "127.0.0.1", 9800, TestEchoServer
+ $n = 0
+ $max = 0
+ 100.times {
+ EM.connect("127.0.0.1", 9800, TestEchoClient) {$n += 1}
+ }
+ }
+ assert_equal(0, $n)
+ assert_equal(100, $max)
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ module TestDatagramServer
+ def receive_data dgm
+ $in = dgm
+ send_data "abcdefghij"
+ end
+ end
+ module TestDatagramClient
+ def initialize port
+ @port = port
+ end
+
+ def post_init
+ send_datagram "1234567890", "127.0.0.1", @port
+ end
+
+ def receive_data dgm
+ $out = dgm
+ EM.stop
+ end
+ end
+
+ def test_datagrams
+ $in = $out = ""
+ EM.run {
+ EM.open_datagram_socket "127.0.0.1", @port, TestDatagramServer
+ EM.open_datagram_socket "127.0.0.1", 0, TestDatagramClient, @port
+ }
+ assert_equal( "1234567890", $in )
+ assert_equal( "abcdefghij", $out )
+ end
+
+ # XXX this test fails randomly...
+ def _test_unix_domain
+ fn = "/tmp/xxx.chain"
+ EM.epoll
+ EM.set_descriptor_table_size 60000
+ EM.run {
+ # The pure-Ruby version won't let us open the socket if the node already exists.
+ # Not sure, that actually may be correct and the compiled version is wrong.
+ # Pure Ruby also oddly won't let us make that many connections. This test used
+ # to run 100 times. Not sure where that lower connection-limit is coming from in
+ # pure Ruby.
+ # Let's not sweat the Unix-ness of the filename, since this test can't possibly
+ # work on Windows anyway.
+ #
+ File.unlink(fn) if File.exist?(fn)
+ EM.start_unix_domain_server fn, TestEchoServer
+ $n = 0
+ $max = 0
+ 50.times {
+ EM.connect_unix_domain(fn, TestEchoClient) {$n += 1}
+ }
+ EM::add_timer(1) { $stderr.puts("test_unix_domain timed out!"); EM::stop }
+ }
+ assert_equal(0, $n)
+ assert_equal(50, $max)
+ ensure
+ File.unlink(fn) if File.exist?(fn)
+ end
+
+ def test_attach_detach
+ EM.epoll
+ EM.run {
+ EM.add_timer(0.01) { EM.stop }
+
+ r, _ = IO.pipe
+
+ # This tests a regression where detach in the same tick as attach crashes EM
+ EM.watch(r) do |connection|
+ connection.detach
+ end
+ }
+
+ assert true
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_error_handler.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_error_handler.rb
new file mode 100644
index 0000000..23c23f7
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_error_handler.rb
@@ -0,0 +1,38 @@
+require 'em_test_helper'
+
+class TestErrorHandler < Test::Unit::TestCase
+ def setup
+ @exception = Class.new(StandardError)
+ end
+
+ def test_error_handler
+ error = nil
+
+ EM.error_handler{ |e|
+ error = e
+ EM.error_handler(nil)
+ EM.stop
+ }
+
+ assert_nothing_raised do
+ EM.run{
+ EM.add_timer(0){
+ raise @exception, 'test'
+ }
+ }
+ end
+
+ assert_equal error.class, @exception
+ assert_equal error.message, 'test'
+ end
+
+ def test_without_error_handler
+ assert_raise @exception do
+ EM.run{
+ EM.add_timer(0){
+ raise @exception, 'test'
+ }
+ }
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_exc.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_exc.rb
new file mode 100644
index 0000000..d9c860a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_exc.rb
@@ -0,0 +1,43 @@
+require 'em_test_helper'
+
+class TestSomeExceptions < Test::Unit::TestCase
+ class DoomedConnectionError < StandardError
+ end
+ class DoomedConnection < EventMachine::Connection
+ def unbind
+ raise DoomedConnectionError
+ end
+ end
+
+ # Read the commentary in EM#run.
+ # This test exercises the ensure block in #run that makes sure
+ # EM#release_machine gets called even if an exception is
+ # thrown within the user code. Without the ensured call to release_machine,
+ # the second call to EM#run will fail with a C++ exception
+ # because the machine wasn't cleaned up properly.
+
+ def test_a
+ assert_raises(RuntimeError) {
+ EM.run {
+ raise "some exception"
+ }
+ }
+ end
+
+ def test_b
+ assert_raises(RuntimeError) {
+ EM.run {
+ raise "some exception"
+ }
+ }
+ end
+
+ def test_exception_on_unbind
+ assert_raises(DoomedConnectionError) {
+ EM.run {
+ EM.connect("localhost", 8888, DoomedConnection)
+ }
+ }
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_file_watch.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_file_watch.rb
new file mode 100644
index 0000000..5602071
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_file_watch.rb
@@ -0,0 +1,85 @@
+require 'em_test_helper'
+require 'tempfile'
+
+class TestFileWatch < Test::Unit::TestCase
+ if windows?
+ def test_watch_file_raises_unsupported_error
+ assert_raises(EM::Unsupported) do
+ EM.run do
+ file = Tempfile.new("fake_file")
+ EM.watch_file(file.path)
+ end
+ end
+ end
+ elsif EM.respond_to? :watch_filename
+ module FileWatcher
+ def file_modified
+ $modified = true
+ end
+ def file_deleted
+ $deleted = true
+ end
+ def unbind
+ $unbind = true
+ EM.stop
+ end
+ end
+
+ def setup
+ EM.kqueue = true if EM.kqueue?
+ end
+
+ def teardown
+ EM.kqueue = false if EM.kqueue?
+ end
+
+ def test_events
+ omit_if(solaris?)
+ EM.run{
+ file = Tempfile.new('em-watch')
+ $tmp_path = file.path
+
+ # watch it
+ watch = EM.watch_file(file.path, FileWatcher)
+ $path = watch.path
+
+ # modify it
+ File.open(file.path, 'w'){ |f| f.puts 'hi' }
+
+ # delete it
+ EM.add_timer(0.01){ file.close; file.delete }
+ }
+
+ assert_equal($path, $tmp_path)
+ assert($modified)
+ assert($deleted)
+ assert($unbind)
+ end
+
+ # Refer: https://github.com/eventmachine/eventmachine/issues/512
+ def test_invalid_signature
+ # This works fine with kqueue, only fails with linux inotify.
+ omit_if(EM.kqueue?)
+
+ EM.run {
+ file = Tempfile.new('foo')
+
+ w1 = EventMachine.watch_file(file.path)
+ w2 = EventMachine.watch_file(file.path)
+
+ assert_raise EventMachine::InvalidSignature do
+ w2.stop_watching
+ end
+
+ EM.stop
+ }
+ end
+ else
+ warn "EM.watch_file not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_watch_file_unsupported
+ assert true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_fork.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_fork.rb
new file mode 100644
index 0000000..8b15bb5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_fork.rb
@@ -0,0 +1,75 @@
+require 'em_test_helper'
+
+class TestFork < Test::Unit::TestCase
+
+ def test_fork_safe
+ omit_if(jruby?)
+ omit_if(windows?)
+
+ fork_pid = nil
+ read, write = IO.pipe
+ EM.run do
+ fork_pid = fork do
+ write.puts "forked"
+ EM.run do
+ EM.next_tick do
+ write.puts "EM ran"
+ EM.stop
+ end
+ end
+ end
+ EM.stop
+ end
+
+ sleep 0.1
+ begin
+ Timeout::timeout 1 do
+ assert_equal "forked\n", read.readline
+ assert_equal "EM ran\n", read.readline
+ end
+ rescue Timeout::Error
+ Process.kill 'TERM', fork_pid
+ flunk "Timeout waiting for next_tick in new fork reactor"
+ end
+ ensure
+ read.close rescue nil
+ write.close rescue nil
+ end
+
+ def test_fork_reactor
+ omit_if(jruby?)
+ omit_if(windows?)
+
+ fork_pid = nil
+ read, write = IO.pipe
+ EM.run do
+ EM.defer do
+ write.puts Process.pid
+ EM.defer do
+ EM.stop
+ end
+ end
+ fork_pid = EM.fork_reactor do
+ EM.defer do
+ write.puts Process.pid
+ EM.stop
+ end
+ end
+ end
+
+ sleep 0.1
+ begin
+ Timeout::timeout 1 do
+ assert_equal Process.pid.to_s, read.readline.chomp
+ assert_equal fork_pid.to_s, read.readline.chomp
+ end
+ rescue Timeout::Error
+ Process.kill 'TERM', fork_pid
+ flunk "Timeout waiting for deferred block in fork_reactor"
+ end
+ ensure
+ read.close rescue nil
+ write.close rescue nil
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_futures.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_futures.rb
new file mode 100644
index 0000000..b494856
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_futures.rb
@@ -0,0 +1,170 @@
+require 'em_test_helper'
+
+class TestFutures < Test::Unit::TestCase
+
+ def setup
+ end
+
+ def teardown
+ end
+
+ def test_future
+ assert_equal(100, EM::Deferrable.future(100) )
+
+ p1 = proc { 100 + 1 }
+ assert_equal(101, EM::Deferrable.future(p1) )
+ end
+
+ class MyFuture
+ include EM::Deferrable
+ def initialize *args
+ super
+ set_deferred_status :succeeded, 40
+ end
+ end
+
+ class MyErrorFuture
+ include EM::Deferrable
+ def initialize *args
+ super
+ set_deferred_status :failed, 41
+ end
+ end
+
+
+ def test_future_1
+ # Call future with one additional argument and it will be treated as a callback.
+ def my_future
+ MyFuture.new
+ end
+
+ value = nil
+ EM::Deferrable.future my_future, proc {|v| value=v}
+ assert_equal( 40, value )
+ end
+
+
+ def test_future_2
+ # Call future with two additional arguments and they will be treated as a callback
+ # and an errback.
+ value = nil
+ EM::Deferrable.future MyErrorFuture.new, nil, proc {|v| value=v}
+ assert_equal( 41, value )
+ end
+
+
+ def test_future_3
+ # Call future with no additional arguments but with a block, and the block will be
+ # treated as a callback.
+ value = nil
+ EM::Deferrable.future MyFuture.new do |v|
+ value=v
+ end
+ assert_equal( 40, value )
+ end
+
+
+ class RecursiveCallback
+ include EM::Deferrable
+ end
+
+ # A Deferrable callback can call #set_deferred_status to change the values
+ # passed to subsequent callbacks.
+ #
+ def test_recursive_callbacks
+ n = 0 # counter assures that all the tests actually run.
+ rc = RecursiveCallback.new
+ rc.callback {|a|
+ assert_equal(100, a)
+ n += 1
+ rc.set_deferred_status :succeeded, 101, 101
+ }
+ rc.callback {|a,b|
+ assert_equal(101, a)
+ assert_equal(101, b)
+ n += 1
+ rc.set_deferred_status :succeeded, 102, 102, 102
+ }
+ rc.callback {|a,b,c|
+ assert_equal(102, a)
+ assert_equal(102, b)
+ assert_equal(102, c)
+ n += 1
+ }
+ rc.set_deferred_status :succeeded, 100
+ assert_equal(3, n)
+ end
+
+ def test_syntactic_sugar
+ rc = RecursiveCallback.new
+ rc.set_deferred_success 100
+ rc.set_deferred_failure 200
+ end
+
+ # It doesn't raise an error to set deferred status more than once.
+ # In fact, this is a desired and useful idiom when it happens INSIDE
+ # a callback or errback.
+ # However, it's less useful otherwise, and in fact would generally be
+ # indicative of a programming error. However, we would like to be resistant
+ # to such errors. So whenever we set deferred status, we also clear BOTH
+ # stacks of handlers.
+ #
+ def test_double_calls
+ s = 0
+ e = 0
+
+ d = EM::DefaultDeferrable.new
+ d.callback {s += 1}
+ d.errback {e += 1}
+
+ d.succeed # We expect the callback to be called, and the errback to be DISCARDED.
+ d.fail # Presumably an error. We expect the errback NOT to be called.
+ d.succeed # We expect the callback to have been discarded and NOT to be called again.
+
+ assert_equal(1, s)
+ assert_equal(0, e)
+ end
+
+ # Adding a callback to a Deferrable that is already in a success state executes the callback
+ # immediately. The same applies to a an errback added to an already-failed Deferrable.
+ # HOWEVER, we expect NOT to be able to add errbacks to succeeded Deferrables, or callbacks
+ # to failed ones.
+ #
+ # We illustrate this with a rather contrived test. The test calls #fail after #succeed,
+ # which ordinarily would not happen in a real program.
+ #
+ # What we're NOT attempting to specify is what happens if a Deferrable is succeeded and then
+ # failed (or vice-versa). Should we then be able to add callbacks/errbacks of the appropriate
+ # type for immediate execution? For now at least, the official answer is "don't do that."
+ #
+ def test_delayed_callbacks
+ s1 = 0
+ s2 = 0
+ e = 0
+
+ d = EM::DefaultDeferrable.new
+ d.callback {s1 += 1}
+
+ d.succeed # Triggers and discards the callback.
+
+ d.callback {s2 += 1} # This callback is executed immediately and discarded.
+
+ d.errback {e += 1} # This errback should be DISCARDED and never execute.
+ d.fail # To prove it, fail and assert e is 0
+
+ assert_equal( [1,1], [s1,s2] )
+ assert_equal( 0, e )
+ end
+
+ def test_timeout
+ n = 0
+ EM.run {
+ d = EM::DefaultDeferrable.new
+ d.callback {n = 1; EM.stop}
+ d.errback {n = 2; EM.stop}
+ d.timeout(0.01)
+ }
+ assert_equal( 2, n )
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_handler_check.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_handler_check.rb
new file mode 100644
index 0000000..c417694
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_handler_check.rb
@@ -0,0 +1,35 @@
+require 'em_test_helper'
+
+class TestHandlerCheck < Test::Unit::TestCase
+
+ class Foo < EM::Connection; end;
+ module TestModule; end;
+
+ def test_with_correct_class
+ assert_nothing_raised do
+ EM.run {
+ EM.connect("127.0.0.1", 80, Foo)
+ EM.stop_event_loop
+ }
+ end
+ end
+
+ def test_with_incorrect_class
+ assert_raise(ArgumentError) do
+ EM.run {
+ EM.connect("127.0.0.1", 80, String)
+ EM.stop_event_loop
+ }
+ end
+ end
+
+ def test_with_module
+ assert_nothing_raised do
+ EM.run {
+ EM.connect("127.0.0.1", 80, TestModule)
+ EM.stop_event_loop
+ }
+ end
+ end
+
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_hc.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_hc.rb
new file mode 100644
index 0000000..28e32c9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_hc.rb
@@ -0,0 +1,155 @@
+require 'em_test_helper'
+
+class TestHeaderAndContentProtocol < Test::Unit::TestCase
+
+ class SimpleTest < EM::P::HeaderAndContentProtocol
+ attr_reader :first_header, :my_headers, :request
+
+ def receive_first_header_line hdr
+ @first_header ||= []
+ @first_header << hdr
+ end
+ def receive_headers hdrs
+ @my_headers ||= []
+ @my_headers << hdrs
+ end
+ def receive_request hdrs, content
+ @request ||= []
+ @request << [hdrs, content]
+ end
+ end
+
+ class StopOnUnbind < EM::Connection
+ def unbind
+ EM.add_timer(0.01) { EM.stop }
+ end
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ def test_no_content
+ the_connection = nil
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
+ the_connection = conn
+ end
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, StopOnUnbind do |c|
+ c.send_data [ "aaa\n", "bbb\r\n", "ccc\n", "\n" ].join
+ c.close_connection_after_writing
+ end
+ }
+ assert_equal( ["aaa"], the_connection.first_header )
+ assert_equal( [%w(aaa bbb ccc)], the_connection.my_headers )
+ assert_equal( [[%w(aaa bbb ccc), ""]], the_connection.request )
+ end
+
+ def test_content
+ the_connection = nil
+ content = "A" * 50
+ headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
+ the_connection = conn
+ end
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, StopOnUnbind do |c|
+ headers.each { |h| c.send_data "#{h}\r\n" }
+ c.send_data "\n"
+ c.send_data content
+ c.close_connection_after_writing
+ end
+ }
+ assert_equal( ["aaa"], the_connection.first_header )
+ assert_equal( [headers], the_connection.my_headers )
+ assert_equal( [[headers, content]], the_connection.request )
+ end
+
+ def test_several_requests
+ the_connection = nil
+ content = "A" * 50
+ headers = ["aaa", "bbb", "Content-length: #{content.length}", "ccc"]
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
+ the_connection = conn
+ end
+ setup_timeout
+
+ EM.connect( "127.0.0.1", @port, StopOnUnbind ) do |c|
+ 5.times do
+ headers.each { |h| c.send_data "#{h}\r\n" }
+ c.send_data "\n"
+ c.send_data content
+ end
+ c.close_connection_after_writing
+ end
+ }
+ assert_equal( ["aaa"] * 5, the_connection.first_header )
+ assert_equal( [headers] * 5, the_connection.my_headers )
+ assert_equal( [[headers, content]] * 5, the_connection.request )
+ end
+
+
+ # def x_test_multiple_content_length_headers
+ # # This is supposed to throw a RuntimeError but it throws a C++ exception instead.
+ # the_connection = nil
+ # content = "A" * 50
+ # headers = ["aaa", "bbb", ["Content-length: #{content.length}"]*2, "ccc"].flatten
+ # EM.run {
+ # EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
+ # the_connection = conn
+ # end
+ # EM.add_timer(4) {raise "test timed out"}
+ # test_proc = proc {
+ # t = TCPSocket.new "127.0.0.1", @port
+ # headers.each {|h| t.write "#{h}\r\n" }
+ # t.write "\n"
+ # t.write content
+ # t.close
+ # }
+ # EM.defer test_proc, proc {
+ # EM.stop
+ # }
+ # }
+ # end
+
+ def test_interpret_headers
+ the_connection = nil
+ content = "A" * 50
+ headers = [
+ "GET / HTTP/1.0",
+ "Accept: aaa",
+ "User-Agent: bbb",
+ "Host: ccc",
+ "x-tempest-header:ddd"
+ ]
+
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, SimpleTest ) do |conn|
+ the_connection = conn
+ end
+ setup_timeout
+
+ EM.connect( "127.0.0.1", @port, StopOnUnbind ) do |c|
+ headers.each { |h| c.send_data "#{h}\r\n" }
+ c.send_data "\n"
+ c.send_data content
+ c.close_connection_after_writing
+ end
+ }
+
+ hsh = the_connection.headers_2_hash( the_connection.my_headers.shift )
+ expect = {
+ :accept => "aaa",
+ :user_agent => "bbb",
+ :host => "ccc",
+ :x_tempest_header => "ddd"
+ }
+ assert_equal(expect, hsh)
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient.rb
new file mode 100644
index 0000000..572a802
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient.rb
@@ -0,0 +1,238 @@
+require 'em_test_helper'
+
+class TestHttpClient < Test::Unit::TestCase
+
+ def setup
+ @port = next_port
+ end
+
+ #-------------------------------------
+
+ def test_http_client
+ ok = false
+ EM.run {
+ c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ c.errback {EM.stop} # necessary, otherwise a failure blocks the test suite forever.
+ }
+ assert ok
+ end
+
+ #-------------------------------------
+
+ def test_http_client_1
+ ok = false
+ EM.run {
+ c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ c.errback {EM.stop}
+ }
+ assert ok
+ end
+
+ #-------------------------------------
+
+ def test_http_client_2
+ ok = false
+ EM.run {
+ c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ c.errback {EM.stop}
+ }
+ assert ok
+ end
+
+
+ #-----------------------------------------
+
+ # Test a server that returns a page with a zero content-length.
+ # This caused an early version of the HTTP client not to generate a response,
+ # causing this test to hang. Observe, there was no problem with responses
+ # lacking a content-length, just when the content-length was zero.
+ #
+ class EmptyContent < EM::Connection
+ def initialize *args
+ super
+ end
+ def receive_data data
+ send_data "HTTP/1.0 404 ...\r\nContent-length: 0\r\n\r\n"
+ close_connection_after_writing
+ end
+ end
+
+ def test_http_empty_content
+ ok = false
+ EM.run {
+ EM.start_server "127.0.0.1", @port, EmptyContent
+ c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ }
+ assert ok
+ end
+
+
+ #---------------------------------------
+
+ class PostContent < EM::P::LineAndTextProtocol
+ def initialize *args
+ super
+ @lines = []
+ end
+ def receive_line line
+ if line.length > 0
+ @lines << line
+ else
+ process_headers
+ end
+ end
+ def receive_binary_data data
+ @post_content = data
+ send_response
+ end
+ def process_headers
+ if @lines.first =~ /\APOST ([^\s]+) HTTP\/1.1\Z/
+ @uri = $1.dup
+ else
+ raise "bad request"
+ end
+
+ @lines.each {|line|
+ if line =~ /\AContent-length:\s*(\d+)\Z/i
+ @content_length = $1.dup.to_i
+ elsif line =~ /\AContent-type:\s*(\d+)\Z/i
+ @content_type = $1.dup
+ end
+ }
+
+ raise "invalid content length" unless @content_length
+ set_binary_mode @content_length
+ end
+ def send_response
+ send_data "HTTP/1.1 200 ...\r\nConnection: close\r\nContent-length: 10\r\nContent-type: text/html\r\n\r\n0123456789"
+ close_connection_after_writing
+ end
+ end
+
+ # TODO, this is WRONG. The handler is asserting an HTTP 1.1 request, but the client
+ # is sending a 1.0 request. Gotta fix the client
+ def test_post
+ response = nil
+ EM.run {
+ EM.start_server '127.0.0.1', @port, PostContent
+ setup_timeout(2)
+ c = silent { EM::P::HttpClient.request(
+ :host => '127.0.0.1',
+ :port => @port,
+ :method => :post,
+ :request => "/aaa",
+ :content => "XYZ",
+ :content_type => "text/plain"
+ )}
+ c.callback {|r|
+ response = r
+ EM.stop
+ }
+ }
+
+ assert_equal( 200, response[:status] )
+ assert_equal( "0123456789", response[:content] )
+ end
+
+
+ # TODO, need a more intelligent cookie tester.
+ # In fact, this whole test-harness needs a beefier server implementation.
+ def test_cookie
+ ok = false
+ EM.run {
+ c = silent { EM::Protocols::HttpClient.send :request, :host => "www.google.com", :port => 80, :cookie=>"aaa=bbb" }
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ c.errback {EM.stop}
+ }
+ assert ok
+ end
+
+ # We can tell the client to send an HTTP/1.0 request (default is 1.1).
+ # This is useful for suppressing chunked responses until those are working.
+ def test_version_1_0
+ ok = false
+ EM.run {
+ c = silent { EM::P::HttpClient.request(
+ :host => "www.google.com",
+ :port => 80,
+ :version => "1.0"
+ )}
+ c.callback {
+ ok = true
+ c.close_connection
+ EM.stop
+ }
+ c.errback {EM.stop}
+ }
+ assert ok
+ end
+
+ #-----------------------------------------
+
+ # Test a server that returns chunked encoding
+ #
+ class ChunkedEncodingContent < EventMachine::Connection
+ def initialize *args
+ super
+ end
+ def receive_data data
+ send_data ["HTTP/1.1 200 OK",
+ "Server: nginx/0.7.67",
+ "Date: Sat, 23 Oct 2010 16:41:32 GMT",
+ "Content-Type: application/json",
+ "Transfer-Encoding: chunked",
+ "Connection: keep-alive",
+ "",
+ "1800",
+ "chunk1" * 1024,
+ "5a",
+ "chunk2" * 15,
+ "0",
+ ""].join("\r\n")
+ close_connection_after_writing
+ end
+ end
+
+ def test_http_chunked_encoding_content
+ ok = false
+ EM.run {
+ EM.start_server "127.0.0.1", @port, ChunkedEncodingContent
+ c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
+ c.callback { |result|
+ if result[:content] == "chunk1" * 1024 + "chunk2" * 15
+ ok = true
+ end
+ c.close_connection
+ EM.stop
+ }
+ }
+ assert ok
+ end
+
+end
+
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient2.rb
new file mode 100644
index 0000000..a00fcbc
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_httpclient2.rb
@@ -0,0 +1,128 @@
+require 'em_test_helper'
+
+class TestHttpClient2 < Test::Unit::TestCase
+ class TestServer < EM::Connection
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ # #connect returns an object which has made a connection to an HTTP server
+ # and exposes methods for making HTTP requests on that connection.
+ # #connect can take either a pair of parameters (a host and a port),
+ # or a single parameter which is a Hash.
+ #
+ def test_connect
+ EM.run {
+ setup_timeout(1)
+ EM.start_server '127.0.0.1', @port, TestServer
+ silent do
+ EM::P::HttpClient2.connect '127.0.0.1', @port
+ EM::P::HttpClient2.connect( :host=>'127.0.0.1', :port=>@port )
+ end
+ EM.stop
+ }
+ end
+
+ def test_bad_port
+ EM.run {
+ setup_timeout(1)
+ EM.start_server '127.0.0.1', @port, TestServer
+ assert_raises( ArgumentError ) {
+ silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" }
+ }
+ EM.stop
+ }
+ end
+
+ def test_bad_server
+ err = nil
+ EM.run {
+ setup_timeout(1)
+ http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 }
+ d = http.get "/"
+ d.errback { err = true; d.internal_error; EM.stop }
+ }
+ assert(err)
+ end
+
+ def test_get
+ content = nil
+ EM.run {
+ setup_timeout(1)
+ http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80, :version => '1.0' }
+ d = http.get "/"
+ d.callback {
+ content = d.content
+ EM.stop
+ }
+ }
+ assert(content)
+ end
+
+ # Not a pipelined request because we wait for one response before we request the next.
+ # XXX this test is broken because it sends the second request to the first connection
+ # XXX right before the connection closes
+ def _test_get_multiple
+ content = nil
+ EM.run {
+ setup_timeout(1)
+ http = silent { EM::P::HttpClient2.connect "google.com", :version => '1.0' }
+ d = http.get "/"
+ d.callback {
+ e = http.get "/"
+ e.callback {
+ content = e.content
+ EM.stop
+ }
+ }
+ }
+ assert(content)
+ end
+
+ def test_get_pipeline
+ headers, headers2 = nil, nil
+ EM.run {
+ setup_timeout(1)
+ http = silent { EM::P::HttpClient2.connect "google.com", 80 }
+ d = http.get("/")
+ d.callback {
+ headers = d.headers
+ }
+ e = http.get("/")
+ e.callback {
+ headers2 = e.headers
+ }
+ EM.tick_loop { EM.stop if headers && headers2 }
+ EM.add_timer(1) { EM.stop }
+ }
+ assert(headers)
+ assert(headers2)
+ end
+
+ def test_authheader
+ EM.run {
+ setup_timeout(1)
+ EM.start_server '127.0.0.1', @port, TestServer
+ http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 }
+ d = http.get :url=>"/", :authorization=>"Basic xxx"
+ d.callback {EM.stop}
+ d.errback {EM.stop}
+ }
+ end
+
+ def test_https_get
+ omit_unless(EM.ssl?)
+ d = nil
+ EM.run {
+ setup_timeout(1)
+ http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :ssl => true, :version => '1.0' }
+ d = http.get "/"
+ d.callback {EM.stop}
+ d.errback {EM.stop}
+ }
+ assert_equal(200, d.status)
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_idle_connection.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_idle_connection.rb
new file mode 100644
index 0000000..bfc57cd
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_idle_connection.rb
@@ -0,0 +1,31 @@
+require 'em_test_helper'
+
+class TestIdleConnection < Test::Unit::TestCase
+ def setup
+ @port = next_port
+ end
+
+ def test_idle_time
+ omit_if(!EM.respond_to?(:get_idle_time))
+
+ a, b = nil, nil
+ EM.run do
+ EM.start_server '127.0.0.1', @port, Module.new
+ conn = EM.connect '127.0.0.1', @port
+ EM.add_timer(0.3) do
+ a = conn.get_idle_time
+ conn.send_data 'a'
+ EM.next_tick do
+ EM.next_tick do
+ b = conn.get_idle_time
+ conn.close_connection
+ EM.stop
+ end
+ end
+ end
+ end
+
+ assert_in_delta 0.3, a, 0.1
+ assert_in_delta 0, b, 0.1
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_inactivity_timeout.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_inactivity_timeout.rb
new file mode 100644
index 0000000..15e39a2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_inactivity_timeout.rb
@@ -0,0 +1,54 @@
+require 'em_test_helper'
+
+class TestInactivityTimeout < Test::Unit::TestCase
+
+ if EM.respond_to? :get_comm_inactivity_timeout
+ def test_default
+ EM.run {
+ c = EM.connect("127.0.0.1", 54321)
+ assert_equal 0.0, c.comm_inactivity_timeout
+ EM.stop
+ }
+ end
+
+ def test_set_and_get
+ EM.run {
+ c = EM.connect("127.0.0.1", 54321)
+ c.comm_inactivity_timeout = 2.5
+ assert_equal 2.5, c.comm_inactivity_timeout
+ EM.stop
+ }
+ end
+
+ def test_for_real
+ start, finish = nil
+
+ timeout_handler = Module.new do
+ define_method :unbind do
+ finish = Time.now
+ EM.stop
+ end
+ end
+
+ EM.run {
+ setup_timeout
+ EM.heartbeat_interval = 0.01
+ EM.start_server("127.0.0.1", 12345)
+ EM.add_timer(0.01) {
+ start = Time.now
+ c = EM.connect("127.0.0.1", 12345, timeout_handler)
+ c.comm_inactivity_timeout = 0.02
+ }
+ }
+
+ assert_in_delta(0.02, (finish - start), 0.02)
+ end
+ else
+ warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_comm_inactivity_timeout_not_implemented
+ assert true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv4.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv4.rb
new file mode 100644
index 0000000..bd11bbf
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv4.rb
@@ -0,0 +1,95 @@
+require 'em_test_helper'
+
+class TestIPv4 < Test::Unit::TestCase
+ # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data.
+ # Timeout in 2 seconds.
+ def test_ipv4_tcp_local_server
+ omit_if(!Test::Unit::TestCase.public_ipv4?)
+
+ @@received_data = nil
+ @local_port = next_port
+ setup_timeout(2)
+
+ EM.run do
+ EM::start_server(@@public_ipv4, @local_port) do |s|
+ def s.receive_data data
+ @@received_data = data
+ EM.stop
+ end
+ end
+
+ EM::connect(@@public_ipv4, @local_port) do |c|
+ c.send_data "ipv4/tcp"
+ end
+ end
+
+ assert_equal "ipv4/tcp", @@received_data
+ end
+
+ # Runs a UDP server in the local IPv4 address, connects to it and sends a specific data.
+ # Timeout in 2 seconds.
+ def test_ipv4_udp_local_server
+ omit_if(!Test::Unit::TestCase.public_ipv4?)
+
+ @@received_data = nil
+ @local_port = next_port
+ setup_timeout(2)
+
+ EM.run do
+ EM::open_datagram_socket(@@public_ipv4, @local_port) do |s|
+ def s.receive_data data
+ @@received_data = data
+ EM.stop
+ end
+ end
+
+ EM::open_datagram_socket(@@public_ipv4, next_port) do |c|
+ c.send_datagram "ipv4/udp", @@public_ipv4, @local_port
+ end
+ end
+
+ assert_equal "ipv4/udp", @@received_data
+ end
+
+ # Try to connect via TCP to an invalid IPv4. EM.connect should raise
+ # EM::ConnectionError.
+ def test_tcp_connect_to_invalid_ipv4
+ omit_if(!Test::Unit::TestCase.public_ipv4?)
+
+ invalid_ipv4 = "9.9:9"
+
+ EM.run do
+ begin
+ error = nil
+ EM.connect(invalid_ipv4, 1234)
+ rescue => e
+ error = e
+ ensure
+ EM.stop
+ assert_equal EM::ConnectionError, (error && error.class)
+ end
+ end
+ end
+
+ # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise
+ # EM::ConnectionError.
+ def test_udp_send_datagram_to_invalid_ipv4
+ omit_if(!Test::Unit::TestCase.public_ipv4?)
+
+ invalid_ipv4 = "9.9:9"
+
+ EM.run do
+ begin
+ error = nil
+ EM.open_datagram_socket(@@public_ipv4, next_port) do |c|
+ c.send_datagram "hello", invalid_ipv4, 1234
+ end
+ rescue => e
+ error = e
+ ensure
+ EM.stop
+ assert_equal EM::ConnectionError, (error && error.class)
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv6.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv6.rb
new file mode 100644
index 0000000..b52fef1
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ipv6.rb
@@ -0,0 +1,107 @@
+require 'em_test_helper'
+
+class TestIPv6 < Test::Unit::TestCase
+
+ if Test::Unit::TestCase.public_ipv6?
+
+ # Runs a TCP server in the local IPv6 address, connects to it and sends a specific data.
+ # Timeout in 2 seconds.
+ def test_ipv6_tcp_local_server
+ @@received_data = nil
+ @local_port = next_port
+ setup_timeout(2)
+
+ EM.run do
+ EM.start_server(@@public_ipv6, @local_port) do |s|
+ def s.receive_data data
+ @@received_data = data
+ EM.stop
+ end
+ end
+
+ EM::connect(@@public_ipv6, @local_port) do |c|
+ def c.unbind(reason)
+ warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed
+ end
+ c.send_data "ipv6/tcp"
+ end
+ end
+
+ assert_equal "ipv6/tcp", @@received_data
+ end
+
+ # Runs a UDP server in the local IPv6 address, connects to it and sends a specific data.
+ # Timeout in 2 seconds.
+ def test_ipv6_udp_local_server
+ @@received_data = nil
+ @local_port = next_port
+ @@remote_ip = nil
+ setup_timeout(2)
+
+ EM.run do
+ EM.open_datagram_socket(@@public_ipv6, @local_port) do |s|
+ def s.receive_data data
+ _port, @@remote_ip = Socket.unpack_sockaddr_in(get_peername)
+ @@received_data = data
+ EM.stop
+ end
+ end
+
+ EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
+ c.send_datagram "ipv6/udp", @@public_ipv6, @local_port
+ end
+ end
+ assert_equal @@remote_ip, @@public_ipv6
+ assert_equal "ipv6/udp", @@received_data
+ end
+
+ # Try to connect via TCP to an invalid IPv6. EM.connect should raise
+ # EM::ConnectionError.
+ def test_tcp_connect_to_invalid_ipv6
+ invalid_ipv6 = "1:A"
+
+ EM.run do
+ begin
+ error = nil
+ EM.connect(invalid_ipv6, 1234)
+ rescue => e
+ error = e
+ ensure
+ EM.stop
+ assert_equal EM::ConnectionError, (error && error.class)
+ end
+ end
+ end
+
+ # Try to send a UDP datagram to an invalid IPv6. EM.send_datagram should raise
+ # EM::ConnectionError.
+ def test_udp_send_datagram_to_invalid_ipv6
+ invalid_ipv6 = "1:A"
+
+ EM.run do
+ begin
+ error = nil
+ EM.open_datagram_socket(@@public_ipv6, next_port) do |c|
+ c.send_datagram "hello", invalid_ipv6, 1234
+ end
+ rescue => e
+ error = e
+ ensure
+ EM.stop
+ assert_equal EM::ConnectionError, (error && error.class)
+ end
+ end
+ end
+
+
+ else
+ warn "no IPv6 in this host, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests.
+ def test_ipv6_unavailable
+ assert true
+ end
+
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_iterator.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_iterator.rb
new file mode 100644
index 0000000..6ec4e46
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_iterator.rb
@@ -0,0 +1,118 @@
+require 'em_test_helper'
+
+class TestIterator < Test::Unit::TestCase
+
+ # By default, format the time with tenths-of-seconds.
+ # Some tests should ask for extra decimal places to ensure
+ # that delays between iterations will receive a changed time.
+ def get_time(n=1)
+ time = EM.current_time
+ time.strftime('%H:%M:%S.') + time.tv_usec.to_s[0, n]
+ end
+
+ def test_default_concurrency
+ items = {}
+ list = 1..10
+ EM.run {
+ EM::Iterator.new(list).each( proc {|num,iter|
+ time = get_time(3)
+ items[time] ||= []
+ items[time] << num
+ EM::Timer.new(0.02) {iter.next}
+ }, proc {EM.stop})
+ }
+ assert_equal(10, items.keys.size)
+ assert_equal(list.to_a.sort, items.values.flatten.sort)
+ end
+
+ def test_default_concurrency_with_a_proc
+ items = {}
+ list = (1..10).to_a
+ original_list = list.dup
+ EM.run {
+ EM::Iterator.new(proc{list.pop || EM::Iterator::Stop}).each( proc {|num,iter|
+ time = get_time(3)
+ items[time] ||= []
+ items[time] << num
+ EM::Timer.new(0.02) {iter.next}
+ }, proc {EM.stop})
+ }
+ assert_equal(10, items.keys.size)
+ assert_equal(original_list.to_a.sort, items.values.flatten.sort)
+ end
+
+ def test_concurrency_bigger_than_list_size
+ items = {}
+ list = [1,2,3]
+ EM.run {
+ EM::Iterator.new(list,10).each(proc {|num,iter|
+ time = get_time
+ items[time] ||= []
+ items[time] << num
+ EM::Timer.new(1) {iter.next}
+ }, proc {EM.stop})
+ }
+ assert_equal(1, items.keys.size)
+ assert_equal(list.to_a.sort, items.values.flatten.sort)
+ end
+
+ def test_changing_concurrency_affects_active_iteration
+ items = {}
+ list = 1..25
+ seen = 0
+ EM.run {
+ i = EM::Iterator.new(list,1)
+ i.each(proc {|num,iter|
+ time = get_time
+ items[time] ||= []
+ items[time] << num
+ if (seen += 1) == 5
+ # The first 5 items will be distinct times
+ # The next 20 items will happen in 2 bursts
+ i.concurrency = 10
+ end
+ EM::Timer.new(0.2) {iter.next}
+ }, proc {EM.stop})
+ }
+ assert_in_delta(7, items.keys.size, 1)
+ assert_equal(list.to_a.sort, items.values.flatten.sort)
+ end
+
+ def test_map
+ list = 100..150
+ EM.run {
+ EM::Iterator.new(list).map(proc{ |num,iter|
+ EM.add_timer(0.01){ iter.return(num) }
+ }, proc{ |results|
+ assert_equal(list.to_a.size, results.size)
+ EM.stop
+ })
+ }
+ end
+
+ def test_inject
+ omit_if(windows?)
+
+ list = %w[ pwd uptime uname date ]
+ EM.run {
+ EM::Iterator.new(list, 2).inject({}, proc{ |hash,cmd,iter|
+ EM.system(cmd){ |output,status|
+ hash[cmd] = status.exitstatus == 0 ? output.strip : nil
+ iter.return(hash)
+ }
+ }, proc{ |results|
+ assert_equal(results.keys.sort, list.sort)
+ EM.stop
+ })
+ }
+ end
+
+ def test_concurrency_is_0
+ EM.run {
+ assert_raise ArgumentError do
+ EM::Iterator.new(1..5,0)
+ end
+ EM.stop
+ }
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_kb.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_kb.rb
new file mode 100644
index 0000000..9c31f5f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_kb.rb
@@ -0,0 +1,28 @@
+require 'em_test_helper'
+
+class TestKeyboardEvents < Test::Unit::TestCase
+
+ module KbHandler
+ include EM::Protocols::LineText2
+ def receive_line d
+ EM::stop if d == "STOP"
+ end
+ end
+
+ # This test doesn't actually do anything useful but is here to
+ # illustrate the usage. If you removed the timer and ran this test
+ # by itself on a console, and then typed into the console, it would
+ # work.
+ # I don't know how to get the test harness to simulate actual keystrokes.
+ # When someone figures that out, then we can make this a real test.
+ #
+ def test_kb
+ omit_if(jruby?)
+ omit_if(!$stdout.tty?) # don't run the test unless it stands a chance of validity.
+ EM.run do
+ EM.open_keyboard KbHandler
+ EM::Timer.new(1) { EM.stop }
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_line_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_line_protocol.rb
new file mode 100644
index 0000000..2067a72
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_line_protocol.rb
@@ -0,0 +1,33 @@
+require 'em_test_helper'
+
+class TestLineProtocol < Test::Unit::TestCase
+ class LineProtocolTestClass
+ include EM::Protocols::LineProtocol
+
+ def lines
+ @lines ||= []
+ end
+
+ def receive_line(line)
+ lines << line
+ end
+ end
+
+ def setup
+ @proto = LineProtocolTestClass.new
+ end
+
+ def test_simple_split_line
+ @proto.receive_data("this is")
+ assert_equal([], @proto.lines)
+
+ @proto.receive_data(" a test\n")
+ assert_equal(["this is a test"], @proto.lines)
+ end
+
+ def test_simple_lines
+ @proto.receive_data("aaa\nbbb\r\nccc\nddd")
+ assert_equal(%w(aaa bbb ccc), @proto.lines)
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp.rb
new file mode 100644
index 0000000..06c2685
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp.rb
@@ -0,0 +1,155 @@
+require 'em_test_helper'
+
+class TestLineAndTextProtocol < Test::Unit::TestCase
+
+ class TLP_LineBuffer < EM::P::LineAndTextProtocol
+ attr_reader :line_buffer
+
+ def initialize
+ super
+ @line_buffer = []
+ end
+
+ def receive_line line
+ @line_buffer << line
+ end
+ end
+
+ module StopClient
+ def set_receive_data(&blk)
+ @rdb = blk
+ end
+
+ def receive_data data
+ @rdb.call(data) if @rdb
+ end
+
+ def unbind
+ EM.add_timer(0.1) { EM.stop }
+ end
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ def test_simple_lines
+ conn = nil
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, TLP_LineBuffer ) do |c|
+ conn = c
+ end
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, StopClient do |c|
+ c.send_data "aaa\nbbb\r\nccc\n"
+ c.close_connection_after_writing
+ end
+ }
+ assert_equal( %w(aaa bbb ccc), conn.line_buffer)
+ end
+
+ #--------------------------------------------------------------------
+
+ class TLP_ErrorMessage < EM::P::LineAndTextProtocol
+ attr_reader :error_message
+
+ def initialize
+ super
+ @error_message = []
+ end
+
+ def receive_line text
+ raise
+ end
+
+ def receive_error text
+ @error_message << text
+ end
+ end
+
+ def test_overlength_lines
+ conn = nil
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, TLP_ErrorMessage ) do |c|
+ conn = c
+ end
+ setup_timeout
+ EM.connect "127.0.0.1", @port, StopClient do |c|
+ c.send_data "a" * (16*1024 + 1)
+ c.send_data "\n"
+ c.close_connection_after_writing
+ end
+
+ }
+ assert_equal( ["overlength line"], conn.error_message )
+ end
+
+
+ #--------------------------------------------------------------------
+
+ class LineAndTextTest < EM::P::LineAndTextProtocol
+ def receive_line line
+ if line =~ /content-length:\s*(\d+)/i
+ @content_length = $1.to_i
+ elsif line.length == 0
+ set_binary_mode @content_length
+ end
+ end
+ def receive_binary_data text
+ send_data "received #{text.length} bytes"
+ close_connection_after_writing
+ end
+ end
+
+ def test_lines_and_text
+ output = ''
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, LineAndTextTest )
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, StopClient do |c|
+ c.set_receive_data { |data| output << data }
+ c.send_data "Content-length: 400\n"
+ c.send_data "\n"
+ c.send_data "A" * 400
+ EM.add_timer(0.1) { c.close_connection_after_writing }
+ end
+ }
+ assert_equal( "received 400 bytes", output )
+ end
+
+ #--------------------------------------------------------------------
+
+
+ class BinaryTextTest < EM::P::LineAndTextProtocol
+ def receive_line line
+ if line =~ /content-length:\s*(\d+)/i
+ set_binary_mode $1.to_i
+ else
+ raise "protocol error"
+ end
+ end
+ def receive_binary_data text
+ send_data "received #{text.length} bytes"
+ close_connection_after_writing
+ end
+ end
+
+ def test_binary_text
+ output = ''
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, BinaryTextTest )
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, StopClient do |c|
+ c.set_receive_data { |data| output << data }
+ c.send_data "Content-length: 10000\n"
+ c.send_data "A" * 10000
+ EM.add_timer(0.1) { c.close_connection_after_writing }
+ end
+ }
+ assert_equal( "received 10000 bytes", output )
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp2.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp2.rb
new file mode 100644
index 0000000..220fcbe
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ltp2.rb
@@ -0,0 +1,332 @@
+require 'em_test_helper'
+
+# TODO!!! Need tests for overlength headers and text bodies.
+
+class TestLineText2 < Test::Unit::TestCase
+
+ # Run each of these tests two ways: passing in the whole test-dataset in one chunk,
+ # and passing it in one character at a time.
+
+ class Basic
+ include EM::Protocols::LineText2
+ attr_reader :lines
+ def receive_line line
+ (@lines ||= []) << line
+ end
+ end
+ def test_basic
+ testdata = "Line 1\nLine 2\r\nLine 3\n"
+
+ a = Basic.new
+ a.receive_data testdata
+ assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
+
+ a = Basic.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( ["Line 1", "Line 2", "Line 3"], a.lines )
+ end
+
+ # The basic test above shows that extra newlines are chomped
+ # This test shows that newlines are preserved if the delimiter isn't \n
+ class PreserveNewlines
+ include EM::Protocols::LineText2
+ attr_reader :lines
+ def initialize *args
+ super
+ @delim = "|"
+ set_delimiter @delim
+ end
+ def receive_line line
+ (@lines ||= []) << line
+ end
+ end
+ def test_preserve_newlines
+ a = PreserveNewlines.new
+ a.receive_data "aaa|bbb|ccc|\n|\r\n| \t ||"
+ assert_equal( ["aaa", "bbb", "ccc", "\n", "\r\n", " \t ", ""], a.lines )
+ end
+
+ class ChangeDelimiter
+ include EM::Protocols::LineText2
+ attr_reader :lines
+ def initialize *args
+ super
+ @delim = "A"
+ set_delimiter @delim
+ end
+ def receive_line line
+ (@lines ||= []) << line
+ set_delimiter( @delim.succ! )
+ end
+ end
+
+ def test_change_delimiter
+ testdata = %Q(LineaALinebBLinecCLinedD)
+
+ a = ChangeDelimiter.new
+ a.receive_data testdata
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
+
+ a = ChangeDelimiter.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
+ end
+
+ class RegexDelimiter
+ include EM::Protocols::LineText2
+ attr_reader :lines
+ def initialize *args
+ super
+ @delim = /[A-D]/
+ set_delimiter @delim
+ end
+ def receive_line line
+ (@lines ||= []) << line
+ end
+ end
+
+ def test_regex_delimiter
+ testdata = %Q(LineaALinebBLinecCLinedD)
+
+ a = RegexDelimiter.new
+ a.receive_data testdata
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
+
+ a = RegexDelimiter.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines )
+ end
+
+ #--
+ # Test two lines followed by an empty line, ten bytes of binary data, then
+ # two more lines.
+
+ class Binary
+ include EM::Protocols::LineText2
+ attr_reader :lines, :body
+ def initialize *args
+ super
+ @lines = []
+ @body = nil
+ end
+ def receive_line ln
+ if ln == ""
+ set_text_mode 10
+ else
+ @lines << ln
+ end
+ end
+ def receive_binary_data data
+ @body = data
+ end
+ end
+
+ def test_binary
+ testdata = %Q(Line 1
+Line 2
+
+0000000000Line 3
+Line 4
+)
+
+ a = Binary.new
+ a.receive_data testdata
+ assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
+ assert_equal( "0000000000", a.body )
+
+ a = Binary.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( ["Line 1", "Line 2", "Line 3", "Line 4"], a.lines)
+ assert_equal( "0000000000", a.body )
+ end
+
+
+ # Test unsized binary data. The expectation is that each chunk of it
+ # will be passed to us as it it received.
+ class UnsizedBinary
+ include EM::Protocols::LineText2
+ attr_reader :n_calls, :body
+ def initialize *args
+ super
+ set_text_mode
+ end
+ def receive_binary_data data
+ @n_calls ||= 0
+ @n_calls += 1
+ (@body ||= "") << data
+ end
+ end
+
+ def test_unsized_binary
+ testdata = "X\0" * 1000
+
+ a = UnsizedBinary.new
+ a.receive_data testdata
+ assert_equal( 1, a.n_calls )
+ assert_equal( testdata, a.body )
+
+ a = UnsizedBinary.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( 2000, a.n_calls )
+ assert_equal( testdata, a.body )
+ end
+
+
+ # Test binary data with a "throw back" into line-mode.
+ class ThrowBack
+ include EM::Protocols::LineText2
+ attr_reader :headers
+ def initialize *args
+ super
+ @headers = []
+ @n_bytes = 0
+ set_text_mode
+ end
+ def receive_binary_data data
+ wanted = 25 - @n_bytes
+ will_take = if data.length > wanted
+ data.length - wanted
+ else
+ data.length
+ end
+ @n_bytes += will_take
+
+ if @n_bytes == 25
+ set_line_mode( data[will_take..-1] )
+ end
+ end
+ def receive_line ln
+ @headers << ln
+ end
+ end
+ def test_throw_back
+ testdata = "Line\n" * 10
+
+ a = ThrowBack.new
+ a.receive_data testdata
+ assert_equal( ["Line"] * 5, a.headers )
+
+ a = ThrowBack.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( ["Line"] * 5, a.headers )
+ end
+
+ # Test multi-character line delimiters.
+ # Also note that the test data has a "tail" with no delimiter, that will be
+ # discarded, but cf. the BinaryTail test.
+ # TODO!!! This test doesn't work in the byte-by-byte case.
+ class Multichar
+ include EM::Protocols::LineText2
+ attr_reader :lines
+ def initialize *args
+ super
+ @lines = []
+ set_delimiter "012"
+ end
+ def receive_line ln
+ @lines << ln
+ end
+ end
+ def test_multichar
+ testdata = "Line012Line012Line012Line"
+
+ a = Multichar.new
+ a.receive_data testdata
+ assert_equal( ["Line"]*3, a.lines )
+
+ a = Multichar.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ # DOESN'T WORK in this case. Multi-character delimiters are broken.
+ #assert_equal( ["Line"]*3, a.lines )
+ end
+
+ # Test a binary "tail," when a sized binary transfer doesn't complete because
+ # of an unbind. We get a partial result.
+ class BinaryTail
+ include EM::Protocols::LineText2
+ attr_reader :data
+ def initialize *args
+ super
+ @data = ""
+ set_text_mode 1000
+ end
+ def receive_binary_data data
+ # we expect to get all the data in one chunk, even in the byte-by-byte case,
+ # because sized transfers by definition give us exactly one call to
+ # #receive_binary_data.
+ @data = data
+ end
+ end
+ def test_binary_tail
+ testdata = "0" * 500
+
+ a = BinaryTail.new
+ a.receive_data testdata
+ a.unbind
+ assert_equal( "0" * 500, a.data )
+
+ a = BinaryTail.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ a.unbind
+ assert_equal( "0" * 500, a.data )
+ end
+
+
+ # Test an end-of-binary call. Arrange to receive binary data but don't bother counting it
+ # as it comes. Rely on getting receive_end_of_binary_data to signal the transition back to
+ # line mode.
+ # At the present time, this isn't strictly necessary with sized binary chunks because by
+ # definition we accumulate them and make exactly one call to receive_binary_data, but
+ # we may want to support a mode in the future that would break up large chunks into multiple
+ # calls.
+ class LazyBinary
+ include EM::Protocols::LineText2
+ attr_reader :data, :end
+ def initialize *args
+ super
+ @data = ""
+ set_text_mode 1000
+ end
+ def receive_binary_data data
+ # we expect to get all the data in one chunk, even in the byte-by-byte case,
+ # because sized transfers by definition give us exactly one call to
+ # #receive_binary_data.
+ @data = data
+ end
+ def receive_end_of_binary_data
+ @end = true
+ end
+ end
+ def test_receive_end_of_binary_data
+ testdata = "_" * 1000
+ a = LazyBinary.new
+ testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) }
+ assert_equal( "_" * 1000, a.data )
+ assert( a.end )
+ end
+
+
+ # This tests a bug fix in which calling set_text_mode failed when called
+ # inside receive_binary_data.
+ #
+ class BinaryPair
+ include EM::Protocols::LineText2
+ attr_reader :sizes
+ def initialize *args
+ super
+ set_text_mode 1
+ @sizes = []
+ end
+ def receive_binary_data dt
+ @sizes << dt.length
+ set_text_mode( (dt.length == 1) ? 2 : 1 )
+ end
+ end
+ def test_binary_pairs
+ test_data = "123" * 5
+ a = BinaryPair.new
+ a.receive_data test_data
+ assert_equal( [1,2,1,2,1,2,1,2,1,2], a.sizes )
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_many_fds.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_many_fds.rb
new file mode 100644
index 0000000..7c126dc
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_many_fds.rb
@@ -0,0 +1,22 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestManyFDs < Test::Unit::TestCase
+ def setup
+ @port = next_port
+ end
+
+ def test_connection_class_cache
+ mod = Module.new
+ a = nil
+ Process.setrlimit(Process::RLIMIT_NOFILE, 4096) rescue nil
+ EM.run {
+ EM.start_server '127.0.0.1', @port, mod
+ 1100.times do
+ a = EM.connect '127.0.0.1', @port, mod
+ assert_kind_of EM::Connection, a
+ end
+ EM.stop
+ }
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_next_tick.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_next_tick.rb
new file mode 100644
index 0000000..9b60359
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_next_tick.rb
@@ -0,0 +1,104 @@
+require 'em_test_helper'
+
+class TestNextTick < Test::Unit::TestCase
+
+ def test_tick_arg
+ pr = proc {EM.stop}
+ EM.run {
+ EM.next_tick pr
+ }
+ assert true
+ end
+
+ def test_tick_block
+ EM.run {
+ EM.next_tick {EM.stop}
+ }
+ assert true
+ end
+
+ # This illustrates the solution to a long-standing problem.
+ # It's now possible to correctly nest calls to EM#run.
+ # See the source code commentary for EM#run for more info.
+ #
+ def test_run_run
+ EM.run {
+ EM.run {
+ EM.next_tick {EM.stop}
+ }
+ }
+ end
+
+ def test_pre_run_queue
+ x = false
+ EM.next_tick { EM.stop; x = true }
+ EM.run { EM.add_timer(0.01) { EM.stop } }
+ assert x
+ end
+
+ def test_cleanup_after_stop
+ x = true
+ EM.run{
+ EM.next_tick{
+ EM.stop
+ EM.next_tick{ x=false }
+ }
+ }
+ EM.run{
+ EM.next_tick{ EM.stop }
+ }
+ assert x
+ end
+
+ # We now support an additional parameter for EM#run.
+ # You can pass two procs to EM#run now. The first is executed as the normal
+ # run block. The second (if given) is scheduled for execution after the
+ # reactor loop completes.
+ # The reason for supporting this is subtle. There has always been an expectation
+ # that EM#run doesn't return until after the reactor loop ends. But now it's
+ # possible to nest calls to EM#run, which means that a nested call WILL
+ # RETURN. In order to write code that will run correctly either way, it's
+ # recommended to put any code which must execute after the reactor completes
+ # in the second parameter.
+ #
+ def test_run_run_2
+ a = proc {EM.stop}
+ b = proc {assert true}
+ EM.run a, b
+ end
+
+
+ # This illustrates that EM#run returns when it's called nested.
+ # This isn't a feature, rather it's something to be wary of when writing code
+ # that must run correctly even if EM#run is called while a reactor is already
+ # running.
+ def test_run_run_3
+ a = []
+ EM.run {
+ EM.run proc {EM.stop}, proc {a << 2}
+ a << 1
+ }
+ assert_equal( [1,2], a )
+ end
+
+
+ def test_schedule_on_reactor_thread
+ x = false
+ EM.run do
+ EM.schedule { x = true }
+ EM.stop
+ end
+ assert x
+ end
+
+ def test_schedule_from_thread
+ x = false
+ EM.run do
+ Thread.new { EM.schedule { x = true } }.join
+ assert !x
+ EM.next_tick { EM.stop }
+ end
+ assert x
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_object_protocol.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_object_protocol.rb
new file mode 100644
index 0000000..b1287ea
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_object_protocol.rb
@@ -0,0 +1,36 @@
+require 'em_test_helper'
+
+class TestObjectProtocol < Test::Unit::TestCase
+ module Server
+ include EM::P::ObjectProtocol
+ def post_init
+ send_object :hello=>'world'
+ end
+ def receive_object obj
+ $server = obj
+ EM.stop
+ end
+ end
+
+ module Client
+ include EM::P::ObjectProtocol
+ def receive_object obj
+ $client = obj
+ send_object 'you_said'=>obj
+ end
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ def test_send_receive
+ EM.run{
+ EM.start_server "127.0.0.1", @port, Server
+ EM.connect "127.0.0.1", @port, Client
+ }
+
+ assert($client == {:hello=>'world'})
+ assert($server == {'you_said'=>{:hello=>'world'}})
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pause.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pause.rb
new file mode 100644
index 0000000..d078a77
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pause.rb
@@ -0,0 +1,107 @@
+require 'em_test_helper'
+
+class TestPause < Test::Unit::TestCase
+ if EM.respond_to? :pause_connection
+ def setup
+ @port = next_port
+ end
+
+ def teardown
+ assert(!EM.reactor_running?)
+ end
+
+ def test_pause_resume
+ server = nil
+
+ s_rx = c_rx = 0
+
+ test_server = Module.new do
+ define_method :post_init do
+ server = self
+ end
+
+ define_method :receive_data do |data|
+ s_rx += 1
+
+ EM.add_periodic_timer(0.01) { send_data 'hi' }
+ send_data 'hi'
+
+ # pause server, now no outgoing data will actually
+ # be sent and no more incoming data will be received
+ pause
+ end
+ end
+
+ test_client = Module.new do
+ def post_init
+ EM.add_periodic_timer(0.01) do
+ send_data 'hello'
+ end
+ end
+
+ define_method :receive_data do |data|
+ c_rx += 1
+ end
+ end
+
+ EM.run do
+ EM.start_server "127.0.0.1", @port, test_server
+ EM.connect "127.0.0.1", @port, test_client
+
+ EM.add_timer(0.05) do
+ assert_equal 1, s_rx
+ assert_equal 0, c_rx
+ assert server.paused?
+
+ # resume server, queued outgoing and incoming data will be flushed
+ server.resume
+
+ assert !server.paused?
+
+ EM.add_timer(0.05) do
+ assert server.paused?
+ assert s_rx > 1
+ assert c_rx > 0
+ EM.stop
+ end
+ end
+ end
+ end
+
+ def test_pause_in_receive_data
+ incoming = []
+
+ test_server = Module.new do
+ define_method(:receive_data) do |data|
+ incoming << data
+ pause
+ EM.add_timer(0.5){ close_connection }
+ end
+ define_method(:unbind) do
+ EM.stop
+ end
+ end
+
+ buf = 'a' * 1024
+
+ EM.run do
+ EM.start_server "127.0.0.1", @port, test_server
+ cli = EM.connect "127.0.0.1", @port
+ 128.times do
+ cli.send_data buf
+ end
+ end
+
+ assert_equal 1, incoming.size
+ assert incoming[0].bytesize > buf.bytesize
+ assert incoming[0].bytesize < buf.bytesize * 128
+ end
+ else
+ warn "EM.pause_connection not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_pause_connection_not_implemented
+ assert true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pending_connect_timeout.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pending_connect_timeout.rb
new file mode 100644
index 0000000..a3f7fa4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pending_connect_timeout.rb
@@ -0,0 +1,52 @@
+require 'em_test_helper'
+
+class TestPendingConnectTimeout < Test::Unit::TestCase
+
+ if EM.respond_to? :get_pending_connect_timeout
+ def test_default
+ EM.run {
+ c = EM.connect("127.0.0.1", 54321)
+ assert_equal 20.0, c.pending_connect_timeout
+ EM.stop
+ }
+ end
+
+ def test_set_and_get
+ EM.run {
+ c = EM.connect("127.0.0.1", 54321)
+ c.pending_connect_timeout = 2.5
+ assert_equal 2.5, c.pending_connect_timeout
+ EM.stop
+ }
+ end
+
+ def test_for_real
+ start, finish = nil
+
+ timeout_handler = Module.new do
+ define_method :unbind do
+ finish = EM.current_time
+ EM.stop
+ end
+ end
+
+ EM.run {
+ setup_timeout
+ EM.heartbeat_interval = 0.1
+ start = EM.current_time
+ c = EM.connect('192.0.2.0', 54321, timeout_handler)
+ c.pending_connect_timeout = 0.2
+ }
+
+ assert_in_delta(0.2, (finish - start), 0.1)
+ end
+ else
+ warn "EM.pending_connect_timeout not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_pending_connect_timeout_not_implemented
+ assert true
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pool.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pool.rb
new file mode 100644
index 0000000..f859a9d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pool.rb
@@ -0,0 +1,196 @@
+require 'em_test_helper'
+
+class TestPool < Test::Unit::TestCase
+ def pool
+ @pool ||= EM::Pool.new
+ end
+
+ def go
+ EM.run { yield }
+ end
+
+ def stop
+ EM.stop
+ end
+
+ def deferrable
+ @deferrable ||= EM::DefaultDeferrable.new
+ end
+
+ def test_supports_more_work_than_resources
+ ran = false
+ go do
+ pool.perform do
+ ran = true
+ deferrable
+ end
+ stop
+ end
+ assert_equal false, ran
+ go do
+ pool.add :resource
+ stop
+ end
+ assert_equal true, ran
+ end
+
+ def test_reques_resources_on_error
+ pooled_res, pooled_res2 = nil
+ pool.add :res
+ go do
+ pool.perform do |res|
+ pooled_res = res
+ deferrable
+ end
+ stop
+ end
+ deferrable.fail
+ go do
+ pool.perform do |res|
+ pooled_res2 = res
+ deferrable
+ end
+ stop
+ end
+ assert_equal :res, pooled_res
+ assert_equal pooled_res, pooled_res2
+ end
+
+ def test_supports_custom_on_error
+ eres = nil
+ pool.on_error do |res|
+ eres = res
+ end
+ performs = []
+ pool.add :res
+ go do
+ pool.perform do |res|
+ performs << res
+ deferrable
+ end
+ pool.perform do |res|
+ performs << res
+ deferrable
+ end
+ deferrable.fail
+ stop
+ end
+ assert_equal :res, eres
+ # manual requeues required when error handler is installed:
+ assert_equal 1, performs.size
+ assert_equal :res, performs.first
+ end
+
+ def test_catches_successful_deferrables
+ performs = []
+ pool.add :res
+ go do
+ pool.perform { |res| performs << res; deferrable }
+ pool.perform { |res| performs << res; deferrable }
+ stop
+ end
+ assert_equal [:res], performs
+ deferrable.succeed
+ go { stop }
+ assert_equal [:res, :res], performs
+ end
+
+ def test_prunes_locked_and_removed_resources
+ performs = []
+ pool.add :res
+ deferrable.succeed
+ go do
+ pool.perform { |res| performs << res; pool.remove res; deferrable }
+ pool.perform { |res| performs << res; pool.remove res; deferrable }
+ stop
+ end
+ assert_equal [:res], performs
+ end
+
+ # Contents is only to be used for inspection of the pool!
+ def test_contents
+ pool.add :res
+ assert_equal [:res], pool.contents
+ # Assert that modifying the contents list does not affect the pools
+ # contents.
+ pool.contents.delete(:res)
+ assert_equal [:res], pool.contents
+ end
+
+ def test_contents_when_perform_errors_and_on_error_is_not_set
+ pool.add :res
+ assert_equal [:res], pool.contents
+
+ pool.perform do |r|
+ d = EM::DefaultDeferrable.new
+ d.fail
+ d
+ end
+
+ EM.run { EM.next_tick { EM.stop } }
+
+ assert_equal [:res], pool.contents
+ end
+
+ def test_contents_when_perform_errors_and_on_error_is_set
+ pool.add :res
+ res = nil
+ pool.on_error do |r|
+ res = r
+ end
+ assert_equal [:res], pool.contents
+
+ pool.perform do |r|
+ d = EM::DefaultDeferrable.new
+ d.fail 'foo'
+ d
+ end
+
+ EM.run { EM.next_tick { EM.stop } }
+
+ assert_equal :res, res
+ assert_equal [], pool.contents
+ end
+
+ def test_num_waiting
+ pool.add :res
+ assert_equal 0, pool.num_waiting
+ pool.perform { |r| EM::DefaultDeferrable.new }
+ assert_equal 0, pool.num_waiting
+ 10.times { pool.perform { |r| EM::DefaultDeferrable.new } }
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal 10, pool.num_waiting
+ end
+
+ def test_exceptions_in_the_work_block_bubble_up_raise_and_fail_the_resource
+ pool.add :res
+
+ res = nil
+ pool.on_error { |r| res = r }
+ pool.perform { raise 'boom' }
+
+ assert_raises(RuntimeError) do
+ EM.run { EM.next_tick { EM.stop } }
+ end
+
+ assert_equal [], pool.contents
+ assert_equal :res, res
+ end
+
+ def test_removed_list_does_not_leak_on_errors
+ pool.add :res
+
+ pool.on_error do |r|
+ # This is actually the wrong thing to do, and not required, but some users
+ # might do it. When they do, they would find that @removed would cause a
+ # slow leak.
+ pool.remove r
+ end
+
+ pool.perform { d = EM::DefaultDeferrable.new; d.fail; d }
+
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal [], pool.instance_variable_get(:@removed)
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_process_watch.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_process_watch.rb
new file mode 100644
index 0000000..6e0c49a
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_process_watch.rb
@@ -0,0 +1,50 @@
+require 'em_test_helper'
+
+if EM.kqueue?
+ class TestProcessWatch < Test::Unit::TestCase
+ module ParentProcessWatcher
+ def process_forked
+ $forked = true
+ end
+ end
+
+ module ChildProcessWatcher
+ def process_exited
+ $exited = true
+ end
+ def unbind
+ $unbind = true
+ EM.stop
+ end
+ end
+
+ def setup
+ EM.kqueue = true
+ end
+
+ def teardown
+ EM.kqueue = false
+ end
+
+ def test_events
+ omit_if(rbx?)
+ omit_if(jruby?)
+ EM.run{
+ # watch ourselves for a fork notification
+ EM.watch_process(Process.pid, ParentProcessWatcher)
+ $fork_pid = fork{ sleep }
+ child = EM.watch_process($fork_pid, ChildProcessWatcher)
+ $pid = child.pid
+
+ EM.add_timer(0.2){
+ Process.kill('TERM', $fork_pid)
+ }
+ }
+
+ assert_equal($pid, $fork_pid)
+ assert($forked)
+ assert($exited)
+ assert($unbind)
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_processes.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_processes.rb
new file mode 100644
index 0000000..dd03cf0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_processes.rb
@@ -0,0 +1,128 @@
+require 'em_test_helper'
+
+class TestProcesses < Test::Unit::TestCase
+
+ if !windows? && !jruby?
+
+ # 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.
+ #
+ def test_deferrable_child_process
+ ls = ""
+ EM.run {
+ d = EM::DeferrableChildProcess.open( "ls -ltr" )
+ d.callback {|data_from_child|
+ ls = data_from_child
+ EM.stop
+ }
+ }
+ assert( ls.length > 0)
+ end
+
+ def setup
+ $out = nil
+ $status = nil
+ end
+
+ def test_em_system
+ EM.run{
+ EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop }
+ }
+
+ assert( $out.length > 0 )
+ assert_equal(0, $status.exitstatus)
+ assert_kind_of(Process::Status, $status)
+ end
+
+ def test_em_system_pid
+ $pids = []
+
+ EM.run{
+ $pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop })
+ }
+
+ assert_equal $pids[0], $pids[1]
+ end
+
+ def test_em_system_with_proc
+ EM.run{
+ EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop })
+ }
+
+ assert( $out.length > 0 )
+ assert_equal(0, $status.exitstatus)
+ assert_kind_of(Process::Status, $status)
+ end
+
+ def test_em_system_with_two_procs
+ EM.run{
+ EM.system('sh', proc{ |process|
+ process.send_data("echo hello\n")
+ process.send_data("exit\n")
+ }, proc{ |out,status|
+ $out = out
+ $status = status
+ EM.stop
+ })
+ }
+
+ assert_equal("hello\n", $out)
+ end
+
+ def test_em_system_cmd_arguments
+ EM.run{
+ EM.system('echo', '1', '2', 'version', proc{ |process|
+ }, proc{ |out,status|
+ $out = out
+ $status = status
+ EM.stop
+ })
+ }
+
+ assert_match(/1 2 version/i, $out)
+ end
+
+ def test_em_system_spaced_arguments
+ EM.run{
+ EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status|
+ $out = out
+ EM.stop
+ })
+ }
+
+ assert_equal("hello\n", $out)
+ end
+
+ def test_em_popen_pause_resume
+ c_rx = 0
+
+ test_client = Module.new do
+ define_method :receive_data do |data|
+ c_rx += 1
+ pause
+ EM.add_timer(0.5) { EM.stop }
+ end
+ end
+
+ EM.run do
+ EM.popen('echo 1', test_client)
+ end
+
+ assert_equal 1, c_rx
+ end
+ else
+ warn "EM.popen not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_popen_unsupported
+ assert true
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_proxy_connection.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_proxy_connection.rb
new file mode 100644
index 0000000..11c0fb4
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_proxy_connection.rb
@@ -0,0 +1,180 @@
+require 'em_test_helper'
+
+class TestProxyConnection < Test::Unit::TestCase
+
+ if EM.respond_to?(:start_proxy)
+ module ProxyConnection
+ def initialize(client, request)
+ @client, @request = client, request
+ end
+
+ def post_init
+ EM::enable_proxy(self, @client)
+ end
+
+ def connection_completed
+ EM.next_tick {
+ send_data @request
+ }
+ end
+
+ def proxy_target_unbound
+ $unbound_early = true
+ EM.stop
+ end
+
+ def unbind
+ $proxied_bytes = self.get_proxied_bytes
+ @client.close_connection_after_writing
+ end
+ end
+
+ module PartialProxyConnection
+ def initialize(client, request, length)
+ @client, @request, @length = client, request, length
+ end
+
+ def post_init
+ EM::enable_proxy(self, @client, 0, @length)
+ end
+
+ def receive_data(data)
+ $unproxied_data = data
+ @client.send_data(data)
+ end
+
+ def connection_completed
+ EM.next_tick {
+ send_data @request
+ }
+ end
+
+ def proxy_target_unbound
+ $unbound_early = true
+ EM.stop
+ end
+
+ def proxy_completed
+ $proxy_completed = true
+ end
+
+ def unbind
+ @client.close_connection_after_writing
+ end
+ end
+
+ module Client
+ def connection_completed
+ send_data "EM rocks!"
+ end
+
+ def receive_data(data)
+ $client_data = data
+ end
+
+ def unbind
+ EM.stop
+ end
+ end
+
+ module Client2
+ include Client
+ def unbind; end
+ end
+
+ module Server
+ def receive_data(data)
+ send_data "I know!" if data == "EM rocks!"
+ close_connection_after_writing
+ end
+ end
+
+ module ProxyServer
+ def initialize port
+ @port = port
+ end
+
+ def receive_data(data)
+ @proxy = EM.connect("127.0.0.1", @port, ProxyConnection, self, data)
+ end
+ end
+
+ module PartialProxyServer
+ def initialize port
+ @port = port
+ end
+
+ def receive_data(data)
+ EM.connect("127.0.0.1", @port, PartialProxyConnection, self, data, 1)
+ end
+ end
+
+ module EarlyClosingProxy
+ def initialize port
+ @port = port
+ end
+
+ def receive_data(data)
+ EM.connect("127.0.0.1", @port, ProxyConnection, self, data)
+ close_connection
+ end
+ end
+
+ def setup
+ @port = next_port
+ @proxy_port = next_port
+ end
+
+ def test_proxy_connection
+ EM.run {
+ EM.start_server("127.0.0.1", @port, Server)
+ EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port)
+ EM.connect("127.0.0.1", @proxy_port, Client)
+ }
+
+ assert_equal("I know!", $client_data)
+ end
+
+ def test_proxied_bytes
+ EM.run {
+ EM.start_server("127.0.0.1", @port, Server)
+ EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port)
+ EM.connect("127.0.0.1", @proxy_port, Client)
+ }
+
+ assert_equal("I know!", $client_data)
+ assert_equal("I know!".bytesize, $proxied_bytes)
+ end
+
+ def test_partial_proxy_connection
+ EM.run {
+ EM.start_server("127.0.0.1", @port, Server)
+ EM.start_server("127.0.0.1", @proxy_port, PartialProxyServer, @port)
+ EM.connect("127.0.0.1", @proxy_port, Client)
+ }
+
+ assert_equal("I know!", $client_data)
+ assert_equal(" know!", $unproxied_data)
+ assert($proxy_completed)
+ end
+
+ def test_early_close
+ $client_data = nil
+ EM.run {
+ EM.start_server("127.0.0.1", @port, Server)
+ EM.start_server("127.0.0.1", @proxy_port, EarlyClosingProxy, @port)
+ EM.connect("127.0.0.1", @proxy_port, Client2)
+ }
+
+ assert($unbound_early)
+ end
+ else
+ warn "EM.start_proxy not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_start_proxy_not_implemented
+ assert !EM.respond_to?(:start_proxy)
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pure.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pure.rb
new file mode 100644
index 0000000..8863a8d
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_pure.rb
@@ -0,0 +1,139 @@
+require 'em_test_helper'
+
+class TestPure < Test::Unit::TestCase
+
+ def setup
+ @port = next_port
+ end
+
+ # These tests are intended to exercise problems that come up in the
+ # pure-Ruby implementation. However, we DON'T constrain them such that
+ # they only run in pure-Ruby. These tests need to work identically in
+ # any implementation.
+
+ #-------------------------------------
+
+ # The EM reactor needs to run down open connections and release other resources
+ # when it stops running. Make sure this happens even if user code throws a Ruby
+ # exception.
+ # If exception handling is incorrect, the second test will fail with a no-bind error
+ # because the TCP server opened in the first test will not have been closed.
+
+ def test_exception_handling_releases_resources
+ exception = Class.new(StandardError)
+
+ 2.times do
+ assert_raises(exception) do
+ EM.run do
+ EM.start_server "127.0.0.1", @port
+ raise exception
+ end
+ end
+ end
+ end
+
+ # Under some circumstances, the pure Ruby library would emit an Errno::ECONNREFUSED
+ # exception on certain kinds of TCP connect-errors.
+ # It's always been something of an open question whether EM should throw an exception
+ # in these cases but the defined answer has always been to catch it the unbind method.
+ # With a connect failure, the latter will always fire, but connection_completed will
+ # never fire. So even though the point is arguable, it's incorrect for the pure Ruby
+ # version to throw an exception.
+ module TestConnrefused
+ def unbind
+ EM.stop
+ end
+ def connection_completed
+ raise "should never get here"
+ end
+ end
+
+ def test_connrefused
+ assert_nothing_raised do
+ EM.run {
+ setup_timeout(2)
+ EM.connect "127.0.0.1", @port, TestConnrefused
+ }
+ end
+ end
+
+ # Make sure connection_completed gets called as expected with TCP clients. This is the
+ # opposite of test_connrefused.
+ # If the test fails, it will hang because EM.stop never gets called.
+ #
+ module TestConnaccepted
+ def connection_completed
+ EM.stop
+ end
+ end
+ def test_connaccepted
+ assert_nothing_raised do
+ EM.run {
+ EM.start_server "127.0.0.1", @port
+ EM.connect "127.0.0.1", @port, TestConnaccepted
+ setup_timeout(1)
+ }
+ end
+ end
+
+ def test_reactor_running
+ a = false
+ EM.run {
+ a = EM.reactor_running?
+ EM.next_tick {EM.stop}
+ }
+ assert a
+ end
+
+ module TLSServer
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ end
+
+ def receive_data(data)
+ $server_received_data = data
+ send_data(data)
+ end
+ end
+
+ module TLSClient
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ end
+
+ def connection_completed
+ send_data('Hello World!')
+ end
+
+ def receive_data(data)
+ $client_received_data = data
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ def test_start_tls
+ $client_handshake_completed, $server_handshake_completed = false, false
+ $client_received_data, $server_received_data = nil, nil
+ EM.run do
+ EM.start_server("127.0.0.1", 16789, TLSServer)
+ EM.connect("127.0.0.1", 16789, TLSClient)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ assert($client_received_data == "Hello World!")
+ assert($server_received_data == "Hello World!")
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_queue.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_queue.rb
new file mode 100644
index 0000000..34278c0
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_queue.rb
@@ -0,0 +1,64 @@
+require 'em_test_helper'
+
+class TestEMQueue < Test::Unit::TestCase
+ def test_queue_push
+ s = 0
+ EM.run do
+ q = EM::Queue.new
+ q.push(1)
+ EM.next_tick { s = q.size; EM.stop }
+ end
+ assert_equal 1, s
+ end
+
+ def test_queue_pop
+ x,y,z = nil
+ EM.run do
+ q = EM::Queue.new
+ q.push(1,2,3)
+ q.pop { |v| x = v }
+ q.pop { |v| y = v }
+ q.pop { |v| z = v; EM.stop }
+ end
+ assert_equal 1, x
+ assert_equal 2, y
+ assert_equal 3, z
+ end
+
+ def test_queue_reactor_thread
+ q = EM::Queue.new
+
+ Thread.new { q.push(1,2,3) }.join
+ assert q.empty?
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal 3, q.size
+
+ x = nil
+ Thread.new { q.pop { |v| x = v } }.join
+ assert_equal nil, x
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal 1, x
+ end
+
+ def test_num_waiting
+ q = EM::Queue.new
+ many = 3
+ many.times { q.pop {} }
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal many, q.num_waiting
+ end
+
+ def test_big_queue
+ EM.run do
+ q = EM::Queue.new
+ 2000.times do |i|
+ q.push(*0..1000)
+ q.pop { |v| assert_equal v, i % 1001 }
+ end
+ q.pop do
+ assert_equal 1_999_999, q.size
+ EM.stop
+ end
+ end
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_resolver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_resolver.rb
new file mode 100644
index 0000000..58ed5f5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_resolver.rb
@@ -0,0 +1,105 @@
+require 'em_test_helper'
+
+class TestResolver < Test::Unit::TestCase
+ def test_nameserver
+ assert_kind_of(String, EM::DNS::Resolver.nameserver)
+ end
+
+ def test_nameservers
+ assert_kind_of(Array, EM::DNS::Resolver.nameservers)
+ end
+
+ def test_hosts
+ assert_kind_of(Hash, EM::DNS::Resolver.hosts)
+
+ # Make sure that blank or comment lines are skipped
+ refute(EM::DNS::Resolver.hosts.include? nil)
+ end
+
+ def test_a
+ pend('FIXME: this test is broken on Windows') if windows?
+
+ EM.run {
+ d = EM::DNS::Resolver.resolve "example.com"
+ d.errback { assert false }
+ d.callback { |r|
+ assert r
+ EM.stop
+ }
+ }
+ end
+
+ def test_bad_host
+ EM.run {
+ d = EM::DNS::Resolver.resolve "asdfasasdf"
+ d.callback { assert false }
+ d.errback { assert true; EM.stop }
+ }
+ end
+
+ def test_garbage
+ assert_raises( ArgumentError ) {
+ EM.run {
+ EM::DNS::Resolver.resolve 123
+ }
+ }
+ end
+
+ # There isn't a public DNS entry like 'example.com' with an A rrset
+ def test_a_pair
+ pend('FIXME: this test is broken on Windows') if windows?
+
+ EM.run {
+ d = EM::DNS::Resolver.resolve "yahoo.com"
+ d.errback { |err| assert false, "failed to resolve yahoo.com: #{err}" }
+ d.callback { |r|
+ assert_kind_of(Array, r)
+ assert r.size > 1, "returned #{r.size} results: #{r.inspect}"
+ EM.stop
+ }
+ }
+ end
+
+ def test_localhost
+ pend('FIXME: this test is broken on Windows') if windows?
+
+ EM.run {
+ d = EM::DNS::Resolver.resolve "localhost"
+ d.errback { assert false }
+ d.callback { |r|
+ assert_include(["127.0.0.1", "::1"], r.first)
+ assert_kind_of(Array, r)
+
+ EM.stop
+ }
+ }
+ end
+
+ def test_timer_cleanup
+ pend('FIXME: this test is broken on Windows') if windows?
+
+ EM.run {
+ d = EM::DNS::Resolver.resolve "example.com"
+ d.errback { |err| assert false, "failed to resolve example.com: #{err}" }
+ d.callback { |r|
+ # This isn't a great test, but it's hard to get more canonical
+ # confirmation that the timer is cancelled
+ assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer))
+
+ EM.stop
+ }
+ }
+ end
+
+ def test_failure_timer_cleanup
+ EM.run {
+ d = EM::DNS::Resolver.resolve "asdfasdf"
+ d.callback { assert false }
+ d.errback {
+ assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer))
+
+ EM.stop
+ }
+ }
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_running.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_running.rb
new file mode 100644
index 0000000..693b390
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_running.rb
@@ -0,0 +1,14 @@
+require 'em_test_helper'
+
+class TestRunning < Test::Unit::TestCase
+ def test_running
+ assert_equal( false, EM::reactor_running? )
+ r = false
+ EM.run {
+ r = EM::reactor_running?
+ EM.stop
+ }
+ assert_equal( true, r )
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sasl.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sasl.rb
new file mode 100644
index 0000000..e80f657
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sasl.rb
@@ -0,0 +1,47 @@
+require 'em_test_helper'
+
+
+class TestSASL < Test::Unit::TestCase
+
+ # SASL authentication is usually done with UNIX-domain sockets, but
+ # we'll use TCP so this test will work on Windows. As far as the
+ # protocol handlers are concerned, there's no difference.
+
+ TestUser,TestPsw = "someone", "password"
+
+ class SaslServer < EM::Connection
+ include EM::Protocols::SASLauth
+ def validate usr, psw, sys, realm
+ usr == TestUser and psw == TestPsw
+ end
+ end
+
+ class SaslClient < EM::Connection
+ include EM::Protocols::SASLauthclient
+ end
+
+ def setup
+ @port = next_port
+ end
+
+ def test_sasl
+ resp = nil
+ EM.run {
+ EM.start_server( "127.0.0.1", @port, SaslServer )
+
+ c = EM.connect( "127.0.0.1", @port, SaslClient )
+ d = c.validate?( TestUser, TestPsw )
+ d.timeout 1
+ d.callback {
+ resp = true
+ EM.stop
+ }
+ d.errback {
+ resp = false
+ EM.stop
+ }
+ }
+ assert_equal( true, resp )
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_send_file.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_send_file.rb
new file mode 100644
index 0000000..a784b50
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_send_file.rb
@@ -0,0 +1,217 @@
+require 'em_test_helper'
+require 'tempfile'
+
+class TestSendFile < Test::Unit::TestCase
+
+ if EM.respond_to?(:send_file_data)
+ module TestModule
+ def initialize filename
+ @filename = filename
+ end
+
+ def post_init
+ send_file_data @filename
+ close_connection_after_writing
+ end
+ end
+
+ module TestClient
+ def data_to(&blk)
+ @data_to = blk
+ end
+
+ def receive_data(data)
+ @data_to.call(data) if @data_to
+ end
+
+ def unbind
+ EM.stop
+ end
+ end
+
+ def setup
+ @file = Tempfile.new("em_test_file")
+ @filename = @file.path
+ @port = next_port
+ end
+
+ def test_send_file
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 5000)
+ }
+
+ data = ''
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, TestModule, @filename
+ setup_timeout
+
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ assert_equal( "A" * 5000, data )
+ end
+
+ # EM::Connection#send_file_data has a strict upper limit on the filesize it will work with.
+ def test_send_large_file
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 1000000)
+ }
+
+ data = ''
+
+ assert_raises(RuntimeError) {
+ EM.run {
+ EM.start_server "127.0.0.1", @port, TestModule, @filename
+ setup_timeout
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+ }
+ end
+
+ module StreamTestModule
+ def initialize filename
+ @filename = filename
+ end
+
+ def post_init
+ EM::Deferrable.future( stream_file_data(@filename)) {
+ close_connection_after_writing
+ }
+ end
+ end
+
+ module ChunkStreamTestModule
+ def initialize filename
+ @filename = filename
+ end
+
+ def post_init
+ EM::Deferrable.future( stream_file_data(@filename, :http_chunks=>true)) {
+ close_connection_after_writing
+ }
+ end
+ end
+
+ def test_stream_file_data
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 1000)
+ }
+
+ data = ''
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, StreamTestModule, @filename
+ setup_timeout
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ assert_equal( "A" * 1000, data )
+ end
+
+ def test_stream_chunked_file_data
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 1000)
+ }
+
+ data = ''
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename
+ setup_timeout
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ assert_equal( "3e8\r\n#{"A" * 1000}\r\n0\r\n\r\n", data )
+ end
+
+ module BadFileTestModule
+ def initialize filename
+ @filename = filename
+ end
+
+ def post_init
+ de = stream_file_data( @filename+".wrong" )
+ de.errback {|msg|
+ send_data msg
+ close_connection_after_writing
+ }
+ end
+ end
+ def test_stream_bad_file
+ data = ''
+ EM.run {
+ EM.start_server "127.0.0.1", @port, BadFileTestModule, @filename
+ setup_timeout(5)
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ assert_equal( "file not found", data )
+ end
+ else
+ warn "EM.send_file_data not implemented, skipping tests in #{__FILE__}"
+
+ # Because some rubies will complain if a TestCase class has no tests
+ def test_em_send_file_data_not_implemented
+ assert !EM.respond_to?(:send_file_data)
+ end
+ end
+
+ begin
+ require 'fastfilereaderext'
+
+ def test_stream_large_file_data
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 10000)
+ }
+
+ data = ''
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, StreamTestModule, @filename
+ setup_timeout
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ assert_equal( "A" * 10000, data )
+ end
+
+ def test_stream_large_chunked_file_data
+ File.open( @filename, "w" ) {|f|
+ f << ("A" * 100000)
+ }
+
+ data = ''
+
+ EM.run {
+ EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename
+ setup_timeout
+ EM.connect "127.0.0.1", @port, TestClient do |c|
+ c.data_to { |d| data << d }
+ end
+ }
+
+ expected = [
+ "4000\r\n#{"A" * 16384}\r\n" * 6,
+ "6a0\r\n#{"A" * 0x6a0}\r\n",
+ "0\r\n\r\n"
+ ].join
+ assert_equal( expected, data )
+ end
+ rescue LoadError
+ warn "require 'fastfilereaderext' failed, skipping tests in #{__FILE__}"
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_servers.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_servers.rb
new file mode 100644
index 0000000..2a17050
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_servers.rb
@@ -0,0 +1,33 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestServers < Test::Unit::TestCase
+
+ def setup
+ @port = next_port
+ end
+
+ def server_alive?
+ port_in_use?(@port)
+ end
+
+ def run_test_stop_server
+ EM.run {
+ sig = EM.start_server("127.0.0.1", @port)
+ assert server_alive?, "Server didn't start"
+ EM.stop_server sig
+ # Give the server some time to shutdown.
+ EM.add_timer(0.1) {
+ assert !server_alive?, "Server didn't stop"
+ EM.stop
+ }
+ }
+ end
+
+ def test_stop_server
+ assert !server_alive?, "Port already in use"
+ 2.times { run_test_stop_server }
+ assert !server_alive?, "Servers didn't stop"
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_shutdown_hooks.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_shutdown_hooks.rb
new file mode 100644
index 0000000..b0e0c5c
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_shutdown_hooks.rb
@@ -0,0 +1,23 @@
+require 'em_test_helper'
+
+class TestShutdownHooks < Test::Unit::TestCase
+ def test_shutdown_hooks
+ r = false
+ EM.run {
+ EM.add_shutdown_hook { r = true }
+ EM.stop
+ }
+ assert_equal( true, r )
+ end
+
+ def test_hook_order
+ r = []
+ EM.run {
+ EM.add_shutdown_hook { r << 2 }
+ EM.add_shutdown_hook { r << 1 }
+ EM.stop
+ }
+ assert_equal( [1, 2], r )
+ end
+end
+
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpclient.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpclient.rb
new file mode 100644
index 0000000..71ed584
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpclient.rb
@@ -0,0 +1,75 @@
+require 'em_test_helper'
+
+class TestSmtpClient < Test::Unit::TestCase
+
+ Localhost = "127.0.0.1"
+ Localport = 9801
+
+ def setup
+ end
+
+ def teardown
+ end
+
+ def test_a
+ # No real tests until we have a server implementation to test against.
+ # This is what the call looks like, though:
+ err = nil
+ EM.run {
+ d = EM::Protocols::SmtpClient.send :domain=>"example.com",
+ :host=>Localhost,
+ :port=>Localport, # optional, defaults 25
+ :starttls=>true,
+ :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",
+ :verbose=>true
+ d.errback {|e|
+ err = e
+ EM.stop
+ }
+ }
+ assert(err)
+ end
+
+ def test_content
+ err = nil
+ EM.run {
+ d = EM::Protocols::SmtpClient.send :domain=>"example.com",
+ :host=>Localhost,
+ :port=>Localport, # optional, defaults 25
+ :starttls=>true,
+ :from=>"sender@example.com",
+ :to=> ["to_1@example.com", "to_2@example.com"],
+ :content => ["Subject: xxx\r\n\r\ndata\r\n.\r\n"],
+ :verbose=>true
+ d.errback {|e|
+ err = e
+ EM.stop
+ }
+ }
+ assert(err)
+ end
+
+
+ EM::Protocols::SmtpClient.__send__(:public, :escape_leading_dots)
+
+ def test_escaping
+ smtp = EM::Protocols::SmtpClient.new :domain => "example.com"
+
+ expectations = {
+ "Hello\r\n" => "Hello\r\n",
+ "\r\n.whatever\r\n" => "\r\n..whatever\r\n",
+ "\r\n.\r\n" => "\r\n..\r\n",
+ "\r\n.\r\n." => "\r\n..\r\n..",
+ ".\r\n.\r\n" => "..\r\n..\r\n",
+ "..\r\n" => "...\r\n"
+ }
+
+ expectations.each do |input, output|
+ assert_equal output, smtp.escape_leading_dots(input)
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpserver.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpserver.rb
new file mode 100644
index 0000000..18c50fe
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_smtpserver.rb
@@ -0,0 +1,57 @@
+require 'em_test_helper'
+
+class TestSmtpServer < Test::Unit::TestCase
+
+ # Don't test on port 25. It requires superuser and there's probably
+ # a mail server already running there anyway.
+ Localhost = "127.0.0.1"
+ Localport = 25001
+
+ # This class is an example of what you need to write in order
+ # to implement a mail server. You override the methods you are
+ # interested in. Some, but not all, of these are illustrated here.
+ #
+ class Mailserver < EM::Protocols::SmtpServer
+
+ attr_reader :my_msg_body, :my_sender, :my_recipients
+
+ def initialize *args
+ super
+ end
+ def receive_sender sender
+ @my_sender = sender
+ #p sender
+ true
+ end
+ def receive_recipient rcpt
+ @my_recipients ||= []
+ @my_recipients << rcpt
+ true
+ end
+ def receive_data_chunk c
+ @my_msg_body = c.last
+ end
+ def connection_ended
+ EM.stop
+ end
+ end
+
+ def test_mail
+ c = nil
+ EM.run {
+ EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn}
+ EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error
+ EM::Protocols::SmtpClient.send :host=>Localhost,
+ :port=>Localport,
+ :domain=>"bogus",
+ :from=>"me@example.com",
+ :to=>"you@example.com",
+ :header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"},
+ :body=>"Not much of interest here."
+
+ }
+ assert_equal( "Not much of interest here.", c.my_msg_body )
+ assert_equal( "<me@example.com>", c.my_sender )
+ assert_equal( ["<you@example.com>"], c.my_recipients )
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sock_opt.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sock_opt.rb
new file mode 100644
index 0000000..60fba35
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_sock_opt.rb
@@ -0,0 +1,54 @@
+require 'em_test_helper'
+require 'socket'
+
+class TestSockOpt < Test::Unit::TestCase
+ def setup
+ assert(!EM.reactor_running?)
+ @port = next_port
+ end
+
+ def teardown
+ assert(!EM.reactor_running?)
+ end
+
+ def test_set_sock_opt
+ omit_if(windows?)
+ omit_if(!EM.respond_to?(:set_sock_opt))
+
+ val = nil
+ test_module = Module.new do
+ define_method :post_init do
+ val = set_sock_opt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true
+ EM.stop
+ end
+ end
+
+ EM.run do
+ EM.start_server '127.0.0.1', @port
+ EM.connect '127.0.0.1', @port, test_module
+ end
+
+ assert_equal 0, val
+ end
+
+ def test_get_sock_opt
+ omit_if(windows?)
+ omit_if(!EM.respond_to?(:set_sock_opt))
+
+ val = nil
+ test_module = Module.new do
+ define_method :connection_completed do
+ val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR
+ EM.stop
+ end
+ end
+
+ EM.run do
+ EM.start_server '127.0.0.1', @port
+ EM.connect '127.0.0.1', @port, test_module
+ end
+
+ assert_equal "\0\0\0\0", val
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb
new file mode 100644
index 0000000..ab0a92f
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_spawn.rb
@@ -0,0 +1,293 @@
+
+require 'em_test_helper'
+
+
+
+class TestSpawn < Test::Unit::TestCase
+
+ # Spawn a process that simply stops the reactor.
+ # Assert that the notification runs after the block that calls it.
+ #
+ def test_stop
+ x = nil
+ EM.run {
+ s = EM.spawn {EM.stop}
+ s.notify
+ x = true
+ }
+ assert x
+ end
+
+
+ # Pass a parameter to a spawned process.
+ #
+ def test_parms
+ val = 5
+ EM.run {
+ s = EM.spawn {|v| val *= v; EM.stop}
+ s.notify 3
+ }
+ assert_equal( 15, val )
+ end
+
+ # Pass multiple parameters to a spawned process.
+ #
+ def test_multiparms
+ val = 5
+ EM.run {
+ s = EM.spawn {|v1,v2| val *= (v1 + v2); EM.stop}
+ s.notify 3,4
+ }
+ assert_equal( 35, val )
+ end
+
+
+ # This test demonstrates that a notification does not happen immediately,
+ # but rather is scheduled sometime after the current code path completes.
+ #
+ def test_race
+ x = 0
+ EM.run {
+ s = EM.spawn {x *= 2; EM.stop}
+ s.notify
+ x = 2
+ }
+ assert_equal( 4, x)
+ end
+
+
+ # Spawn a process and notify it 25 times to run fibonacci
+ # on a pair of global variables.
+ #
+ def test_fibonacci
+ x = 1
+ y = 1
+ EM.run {
+ s = EM.spawn {x,y = y,x+y}
+ 25.times {s.notify}
+
+ t = EM.spawn {EM.stop}
+ t.notify
+ }
+ assert_equal( 121393, x)
+ assert_equal( 196418, y)
+ end
+
+ # This one spawns 25 distinct processes, and notifies each one once,
+ # rather than notifying a single process 25 times.
+ #
+ def test_another_fibonacci
+ x = 1
+ y = 1
+ EM.run {
+ 25.times {
+ s = EM.spawn {x,y = y,x+y}
+ s.notify
+ }
+
+ t = EM.spawn {EM.stop}
+ t.notify
+ }
+ assert_equal( 121393, x)
+ assert_equal( 196418, y)
+ end
+
+
+ # Make a chain of processes that notify each other in turn
+ # with intermediate fibonacci results. The final process in
+ # the chain stops the loop and returns the result.
+ #
+ def test_fibonacci_chain
+ a,b = nil
+
+ EM.run {
+ nextpid = EM.spawn {|x,y|
+ a,b = x,y
+ EM.stop
+ }
+
+ 25.times {
+ n = nextpid
+ nextpid = EM.spawn {|x,y| n.notify( y, x+y )}
+ }
+
+ nextpid.notify( 1, 1 )
+ }
+
+ assert_equal( 121393, a)
+ assert_equal( 196418, b)
+ end
+
+
+ # EM#yield gives a spawed process to yield control to other processes
+ # (in other words, to stop running), and to specify a different code block
+ # that will run on its next notification.
+ #
+ def test_yield
+ a = 0
+ EM.run {
+ n = EM.spawn {
+ a += 10
+ EM.yield {
+ a += 20
+ EM.yield {
+ a += 30
+ EM.stop
+ }
+ }
+ }
+ n.notify
+ n.notify
+ n.notify
+ }
+ assert_equal( 60, a )
+ end
+
+ # EM#yield_and_notify behaves like EM#yield, except that it also notifies the
+ # yielding process. This may sound trivial, since the yield block will run very
+ # shortly after with no action by the program, but this actually can be very useful,
+ # because it causes the reactor core to execute once before the yielding process
+ # gets control back. So it can be used to allow heavily-used network connections
+ # to clear buffers, or allow other processes to process their notifications.
+ #
+ # Notice in this test code that only a simple notify is needed at the bottom
+ # of the initial block. Even so, all of the yielded blocks will execute.
+ #
+ def test_yield_and_notify
+ a = 0
+ EM.run {
+ n = EM.spawn {
+ a += 10
+ EM.yield_and_notify {
+ a += 20
+ EM.yield_and_notify {
+ a += 30
+ EM.stop
+ }
+ }
+ }
+ n.notify
+ }
+ assert_equal( 60, a )
+ end
+
+ # resume is an alias for notify.
+ #
+ def test_resume
+ EM.run {
+ n = EM.spawn {EM.stop}
+ n.resume
+ }
+ assert true
+ end
+
+ # run is an idiomatic alias for notify.
+ #
+ def test_run
+ EM.run {
+ (EM.spawn {EM.stop}).run
+ }
+ assert true
+ end
+
+
+ # Clones the ping-pong example from the Erlang tutorial, in much less code.
+ # Illustrates that a spawned block executes in the context of a SpawnableObject.
+ # (Meaning, we can pass self as a parameter to another process that can then
+ # notify us.)
+ #
+ def test_ping_pong
+ n_pongs = 0
+ EM.run {
+ pong = EM.spawn {|x, ping|
+ n_pongs += 1
+ ping.notify( x-1 )
+ }
+ ping = EM.spawn {|x|
+ if x > 0
+ pong.notify x, self
+ else
+ EM.stop
+ end
+ }
+ ping.notify 3
+ }
+ assert_equal( 3, n_pongs )
+ end
+
+ # Illustrates that you can call notify inside a notification, and it will cause
+ # the currently-executing process to be re-notified. Of course, the new notification
+ # won't run until sometime after the current one completes.
+ #
+ def test_self_notify
+ n = 0
+ EM.run {
+ pid = EM.spawn {|x|
+ if x > 0
+ n += x
+ notify( x-1 )
+ else
+ EM.stop
+ end
+ }
+ pid.notify 3
+ }
+ assert_equal( 6, n )
+ end
+
+
+ # Illustrates that the block passed to #spawn executes in the context of a
+ # SpawnedProcess object, NOT in the local context. This can often be deceptive.
+ #
+ class BlockScopeTest
+ attr_reader :var
+ def run
+ # The following line correctly raises a NameError.
+ # The problem is that the programmer expected the spawned block to
+ # execute in the local context, but it doesn't.
+ #
+ # (EM.spawn { do_something }).notify ### NO! BAD!
+
+
+
+ # The following line correctly passes self as a parameter to the
+ # notified process.
+ #
+ (EM.spawn {|obj| obj.do_something }).notify(self)
+
+
+
+ # Here's another way to do it. This works because "myself" is bound
+ # in the local scope, unlike "self," so the spawned block sees it.
+ #
+ myself = self
+ (EM.spawn { myself.do_something }).notify
+
+
+
+ # And we end the loop.
+ # This is a tangential point, but observe that #notify never blocks.
+ # It merely appends a message to the internal queue of a spawned process
+ # and returns. As it turns out, the reactor processes notifications for ALL
+ # spawned processes in the order that #notify is called. So there is a
+ # reasonable expectation that the process which stops the reactor will
+ # execute after the previous ones in this method. HOWEVER, this is NOT
+ # a documented behavior and is subject to change.
+ #
+ (EM.spawn {EM.stop}).notify
+ end
+ def do_something
+ @var ||= 0
+ @var += 100
+ end
+ end
+
+ def test_block_scope
+ bs = BlockScopeTest.new
+ EM.run {
+ bs.run
+ }
+ assert_equal( 200, bs.var )
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_args.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_args.rb
new file mode 100644
index 0000000..d337628
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_args.rb
@@ -0,0 +1,78 @@
+require "test/unit"
+require 'tempfile'
+
+require 'em_test_helper'
+
+module EM
+ def self._set_mocks
+ class <<self
+ alias set_tls_parms_old set_tls_parms
+ alias start_tls_old start_tls
+ begin
+ old, $VERBOSE = $VERBOSE, nil
+ def set_tls_parms *args; end
+ def start_tls *args; end
+ ensure
+ $VERBOSE = old
+ end
+ end
+ end
+
+ def self._clear_mocks
+ class <<self
+ begin
+ old, $VERBOSE = $VERBOSE, nil
+ alias set_tls_parms set_tls_parms_old
+ alias start_tls start_tls_old
+ ensure
+ $VERBOSE = old
+ end
+ end
+ end
+end
+
+
+
+class TestSslArgs < Test::Unit::TestCase
+ def setup
+ EM._set_mocks
+ end
+
+ def teardown
+ EM._clear_mocks
+ end
+
+ def test_tls_params_file_doesnt_exist
+ priv_file, cert_file = 'foo_priv_key', 'bar_cert_file'
+ [priv_file, cert_file].all? do |f|
+ assert(!File.exist?(f), "Cert file #{f} seems to exist, and should not for the tests")
+ end
+
+ # associate_callback_target is a pain! (build!)
+ conn = EM::Connection.new('foo')
+
+ assert_raises(EM::FileNotFoundException) do
+ conn.start_tls(:private_key_file => priv_file)
+ end
+ assert_raises(EM::FileNotFoundException) do
+ conn.start_tls(:cert_chain_file => cert_file)
+ end
+ assert_raises(EM::FileNotFoundException) do
+ conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file)
+ end
+ end
+
+ def test_tls_params_file_does_exist
+ priv_file = Tempfile.new('em_test')
+ cert_file = Tempfile.new('em_test')
+ priv_file_path = priv_file.path
+ cert_file_path = cert_file.path
+ conn = EM::Connection.new('foo')
+ params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path}
+ begin
+ conn.start_tls params
+ rescue Object
+ assert(false, 'should not have raised an exception')
+ end
+ end
+end if EM.ssl?
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_dhparam.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_dhparam.rb
new file mode 100644
index 0000000..85f52d2
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_dhparam.rb
@@ -0,0 +1,84 @@
+require 'em_test_helper'
+
+class TestSslDhParam < Test::Unit::TestCase
+ def setup
+ $dir = File.dirname(File.expand_path(__FILE__)) + '/'
+ $dhparam_file = File.join($dir, 'dhparam.pem')
+ end
+
+ module Client
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ $client_cipher_name = get_cipher_name
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ module Server
+ def post_init
+ start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH")
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ $server_cipher_name = get_cipher_name
+ end
+ end
+
+ module NoDhServer
+ def post_init
+ start_tls(:cipher_list => "DHE,EDH")
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ $server_cipher_name = get_cipher_name
+ end
+ end
+
+ def test_no_dhparam
+ omit_unless(EM.ssl?)
+ omit_if(EM.library_type == :pure_ruby) # DH will work with defaults
+ omit_if(rbx?)
+
+ $client_handshake_completed, $server_handshake_completed = false, false
+ $server_cipher_name, $client_cipher_name = nil, nil
+
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, NoDhServer)
+ EM.connect("127.0.0.1", 16784, Client)
+ }
+
+ assert(!$client_handshake_completed)
+ assert(!$server_handshake_completed)
+ end
+
+ def test_dhparam
+ omit_unless(EM.ssl?)
+ omit_if(rbx?)
+
+ $client_handshake_completed, $server_handshake_completed = false, false
+ $server_cipher_name, $client_cipher_name = nil, nil
+
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, Server)
+ EM.connect("127.0.0.1", 16784, Client)
+ }
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+
+ assert($client_cipher_name.length > 0)
+ assert_equal($client_cipher_name, $server_cipher_name)
+
+ assert_match(/^(DHE|EDH)/, $client_cipher_name)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_ecdh_curve.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_ecdh_curve.rb
new file mode 100644
index 0000000..8dc167b
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_ecdh_curve.rb
@@ -0,0 +1,80 @@
+require 'em_test_helper'
+
+class TestSslEcdhCurve < Test::Unit::TestCase
+ module Client
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ $client_cipher_name = get_cipher_name
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ module Server
+ def post_init
+ start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH")
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ $server_cipher_name = get_cipher_name
+ end
+ end
+
+ module NoCurveServer
+ def post_init
+ start_tls(:cipher_list => "ECDH")
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ $server_cipher_name = get_cipher_name
+ end
+ end
+
+ def test_no_ecdh_curve
+ omit_unless(EM.ssl?)
+ omit_if(rbx?)
+
+ $client_handshake_completed, $server_handshake_completed = false, false
+
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, NoCurveServer)
+ EM.connect("127.0.0.1", 16784, Client)
+ }
+
+ assert(!$client_handshake_completed)
+ assert(!$server_handshake_completed)
+ end
+
+ def test_ecdh_curve
+ omit_unless(EM.ssl?)
+ omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0")
+ omit_if(rbx?)
+
+ $client_handshake_completed, $server_handshake_completed = false, false
+ $server_cipher_name, $client_cipher_name = nil, nil
+
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, Server)
+ EM.connect("127.0.0.1", 16784, Client)
+ }
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+
+ assert($client_cipher_name.length > 0)
+ assert_equal($client_cipher_name, $server_cipher_name)
+
+ assert_match(/^(AECDH|ECDHE)/, $client_cipher_name)
+ end
+
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_extensions.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_extensions.rb
new file mode 100644
index 0000000..0610ba8
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_extensions.rb
@@ -0,0 +1,49 @@
+require 'em_test_helper'
+
+require 'socket'
+require 'openssl'
+
+if EM.ssl?
+ class TestSslExtensions < Test::Unit::TestCase
+
+ module Client
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+
+ def post_init
+ start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com')
+ end
+ end
+
+ module Server
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ $server_sni_hostname = get_sni_hostname
+ end
+
+ def post_init
+ start_tls(:ssl_version => :TLSv1)
+ end
+ end
+
+ def test_tlsext_sni_hostname
+ $server_handshake_completed = false
+
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, Server)
+ EM.connect("127.0.0.1", 16784, Client)
+ end
+
+ assert($server_handshake_completed)
+ assert_equal('example.com', $server_sni_hostname)
+ end
+ end
+else
+ warn "EM built without SSL support, skipping tests in #{__FILE__}"
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_methods.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_methods.rb
new file mode 100644
index 0000000..c2e5744
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_methods.rb
@@ -0,0 +1,65 @@
+require 'em_test_helper'
+
+class TestSSLMethods < Test::Unit::TestCase
+
+ module ServerHandler
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $server_called_back = true
+ $server_cert_value = get_peer_cert
+ $server_cipher_bits = get_cipher_bits
+ $server_cipher_name = get_cipher_name
+ $server_cipher_protocol = get_cipher_protocol
+ end
+ end
+
+ module ClientHandler
+ def post_init
+ start_tls
+ end
+
+ def ssl_handshake_completed
+ $client_called_back = true
+ $client_cert_value = get_peer_cert
+ $client_cipher_bits = get_cipher_bits
+ $client_cipher_name = get_cipher_name
+ $client_cipher_protocol = get_cipher_protocol
+ EM.stop_event_loop
+ end
+ end
+
+ def test_ssl_methods
+ omit_unless(EM.ssl?)
+ omit_if(rbx?)
+ $server_called_back, $client_called_back = false, false
+ $server_cert_value, $client_cert_value = nil, nil
+ $server_cipher_bits, $client_cipher_bits = nil, nil
+ $server_cipher_name, $client_cipher_name = nil, nil
+ $server_cipher_protocol, $client_cipher_protocol = nil, nil
+
+ EM.run {
+ EM.start_server("127.0.0.1", 9999, ServerHandler)
+ EM.connect("127.0.0.1", 9999, ClientHandler)
+ }
+
+ assert($server_called_back)
+ assert($client_called_back)
+
+ assert($server_cert_value.is_a?(NilClass))
+ assert($client_cert_value.is_a?(String))
+
+ assert($client_cipher_bits > 0)
+ assert_equal($client_cipher_bits, $server_cipher_bits)
+
+ assert($client_cipher_name.length > 0)
+ assert_match(/AES/, $client_cipher_name)
+ assert_equal($client_cipher_name, $server_cipher_name)
+
+ assert_match(/TLS/, $client_cipher_protocol)
+ assert_equal($client_cipher_protocol, $server_cipher_protocol)
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_protocols.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_protocols.rb
new file mode 100644
index 0000000..bcb6824
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_protocols.rb
@@ -0,0 +1,246 @@
+require 'em_test_helper'
+
+require 'socket'
+require 'openssl'
+
+if EM.ssl?
+ class TestSslProtocols < Test::Unit::TestCase
+
+ module Client
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ module Server
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ end
+ end
+
+ module ClientAny
+ include Client
+ def post_init
+ start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
+ end
+ end
+
+ module ClientDefault
+ include Client
+ def post_init
+ start_tls
+ end
+ end
+
+ module ClientSSLv3
+ include Client
+ def post_init
+ start_tls(:ssl_version => %w(SSLv3))
+ end
+ end
+
+ module ServerSSLv3
+ include Server
+ def post_init
+ start_tls(:ssl_version => %w(SSLv3))
+ end
+ end
+
+ module ServerTLSv1CaseInsensitive
+ include Server
+ def post_init
+ start_tls(:ssl_version => %w(tlsv1))
+ end
+ end
+
+ module ServerAny
+ include Server
+ def post_init
+ start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2))
+ end
+ end
+
+ module ServerDefault
+ include Server
+ def post_init
+ start_tls
+ end
+ end
+
+ module InvalidProtocol
+ include Client
+ def post_init
+ start_tls(:ssl_version => %w(tlsv1 badinput))
+ end
+ end
+
+ def test_invalid_ssl_version
+ assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, InvalidProtocol)
+ EM.connect("127.0.0.1", 16784, InvalidProtocol)
+ end
+ end
+ end
+
+ def test_any_to_v3
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerSSLv3)
+ EM.connect("127.0.0.1", 16784, ClientAny)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_case_insensitivity
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive)
+ EM.connect("127.0.0.1", 16784, ClientAny)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_v3_to_any
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerAny)
+ EM.connect("127.0.0.1", 16784, ClientSSLv3)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_v3_to_v3
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerSSLv3)
+ EM.connect("127.0.0.1", 16784, ClientSSLv3)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_any_to_any
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerAny)
+ EM.connect("127.0.0.1", 16784, ClientAny)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_default_to_default
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run do
+ EM.start_server("127.0.0.1", 16784, ServerDefault)
+ EM.connect("127.0.0.1", 16784, ClientDefault)
+ end
+
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ module ServerV3StopAfterHandshake
+ def post_init
+ start_tls(:ssl_version => %w(SSLv3))
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ EM.stop_event_loop
+ end
+ end
+
+ module ServerTLSv1StopAfterHandshake
+ def post_init
+ start_tls(:ssl_version => %w(TLSv1))
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ EM.stop_event_loop
+ end
+ end
+
+ def test_v3_with_external_client
+ $server_handshake_completed = false
+ EM.run do
+ setup_timeout(2)
+ EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake)
+ EM.defer do
+ sock = TCPSocket.new("127.0.0.1", 16784)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :SSLv3_client
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.connect
+ ssl.close rescue nil
+ sock.close rescue nil
+ end
+ end
+
+ assert($server_handshake_completed)
+ end
+
+ def test_tlsv1_with_external_client
+ $server_handshake_completed = false
+ EM.run do
+ setup_timeout(2)
+ EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
+ EM.defer do
+ sock = TCPSocket.new("127.0.0.1", 16784)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :TLSv1_client
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ ssl.connect
+ ssl.close rescue nil
+ sock.close rescue nil
+ end
+ end
+
+ assert($server_handshake_completed)
+ end
+
+ def test_tlsv1_required_with_external_client
+ $server_handshake_completed = false
+
+ EM.run do
+ n = 0
+ EM.add_periodic_timer(0.5) do
+ n += 1
+ (EM.stop rescue nil) if n == 2
+ end
+ EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake)
+ EM.defer do
+ sock = TCPSocket.new("127.0.0.1", 16784)
+ ctx = OpenSSL::SSL::SSLContext.new
+ ctx.ssl_version = :SSLv3_client
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
+ assert_raise OpenSSL::SSL::SSLError do
+ ssl.connect
+ end
+ ssl.close rescue nil
+ sock.close rescue nil
+ EM.stop rescue nil
+ end
+ end
+
+ assert(!$server_handshake_completed)
+ end
+ end
+else
+ warn "EM built without SSL support, skipping tests in #{__FILE__}"
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_verify.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_verify.rb
new file mode 100644
index 0000000..a6d8fca
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ssl_verify.rb
@@ -0,0 +1,128 @@
+require 'em_test_helper'
+
+class TestSslVerify < Test::Unit::TestCase
+ def setup
+ $dir = File.dirname(File.expand_path(__FILE__)) + '/'
+ $cert_from_file = File.read($dir+'client.crt')
+ end
+
+ module ClientNoCert
+ def connection_completed
+ start_tls()
+ end
+
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ module Client
+ def connection_completed
+ start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')
+ end
+
+ def ssl_handshake_completed
+ $client_handshake_completed = true
+ close_connection
+ end
+
+ def unbind
+ EM.stop_event_loop
+ end
+ end
+
+ module AcceptServer
+ def post_init
+ start_tls(:verify_peer => true)
+ end
+
+ def ssl_verify_peer(cert)
+ $cert_from_server = cert
+ true
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ end
+ end
+
+ module DenyServer
+ def post_init
+ start_tls(:verify_peer => true)
+ end
+
+ def ssl_verify_peer(cert)
+ $cert_from_server = 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
+
+ module FailServerNoPeerCert
+ def post_init
+ start_tls(:verify_peer => true, :fail_if_no_peer_cert => true)
+ end
+
+ def ssl_verify_peer(cert)
+ raise "Verify peer should not get called for a client without a certificate"
+ end
+
+ def ssl_handshake_completed
+ $server_handshake_completed = true
+ end
+ end
+
+ def test_fail_no_peer_cert
+ omit_unless(EM.ssl?)
+ omit_if(rbx?)
+
+ $client_handshake_completed, $server_handshake_completed = false, false
+
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, FailServerNoPeerCert)
+ EM.connect("127.0.0.1", 16784, ClientNoCert)
+ }
+
+ assert(!$client_handshake_completed)
+ assert(!$server_handshake_completed)
+ end
+
+ def test_accept_server
+ omit_unless(EM.ssl?)
+ omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain
+ omit_if(rbx?)
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, AcceptServer)
+ EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature")
+ }
+
+ assert_equal($cert_from_file, $cert_from_server)
+ assert($client_handshake_completed)
+ assert($server_handshake_completed)
+ end
+
+ def test_deny_server
+ omit_unless(EM.ssl?)
+ omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain
+ omit_if(rbx?)
+ $client_handshake_completed, $server_handshake_completed = false, false
+ EM.run {
+ EM.start_server("127.0.0.1", 16784, DenyServer)
+ EM.connect("127.0.0.1", 16784, Client)
+ }
+
+ assert_equal($cert_from_file, $cert_from_server)
+ assert(!$client_handshake_completed)
+ assert(!$server_handshake_completed)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_stomp.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_stomp.rb
new file mode 100644
index 0000000..53c0502
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_stomp.rb
@@ -0,0 +1,38 @@
+require 'em_test_helper'
+
+class TestStomp < Test::Unit::TestCase
+ CONTENT_LENGTH_REGEX = /^content-length: (\d+)$/
+
+ def bytesize(str)
+ str = str.to_s
+ size = str.bytesize if str.respond_to?(:bytesize) # bytesize added in 1.9
+ size || str.size
+ end
+
+ class TStomp
+ include EM::P::Stomp
+
+ def last_sent_content_length
+ @sent && Integer(@sent[CONTENT_LENGTH_REGEX, 1])
+ end
+
+ def send_data(string)
+ @sent = string
+ end
+ end
+
+ def test_content_length_in_bytes
+ connection = TStomp.new
+
+ queue = "queue"
+ failure_message = "header content-length is not the byte size of last sent body"
+
+ body = "test"
+ connection.send queue, body
+ assert_equal bytesize(body), connection.last_sent_content_length, failure_message
+
+ body = "test\u221A"
+ connection.send queue, body
+ assert_equal bytesize(body), connection.last_sent_content_length, failure_message
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_system.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_system.rb
new file mode 100644
index 0000000..fbbe2c9
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_system.rb
@@ -0,0 +1,46 @@
+# coding: utf-8
+require 'em_test_helper'
+
+class TestSystem < Test::Unit::TestCase
+ def setup
+ @filename = File.expand_path("../я манал dump.txt", __FILE__)
+ @test_data = 'a' * 100
+ File.open(@filename, 'w'){|f| f.write(@test_data)}
+ end
+
+ def test_system
+ omit_if(windows?)
+
+ result = nil
+ status = nil
+ EM.run {
+ EM.system('cat', @filename){|out, state|
+ result = out
+ status = state.exitstatus
+ EM.stop
+ }
+ }
+ assert_equal(0, status)
+ assert_equal(@test_data, result)
+ end
+
+ def test_system_with_string
+ omit_if(windows?)
+
+ result = nil
+ status = nil
+ EM.run {
+ EM.system("cat '#@filename'"){|out, state|
+ result = out
+ status = state.exitstatus
+ EM.stop
+ }
+ }
+ assert_equal(0, status)
+ assert_equal(@test_data, result)
+ end
+
+ def teardown
+ File.unlink(@filename)
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_threaded_resource.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_threaded_resource.rb
new file mode 100644
index 0000000..9bb39c6
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_threaded_resource.rb
@@ -0,0 +1,63 @@
+require 'em_test_helper'
+
+class TestThreadedResource < Test::Unit::TestCase
+ def object
+ @object ||= {}
+ end
+
+ def resource
+ @resource = EM::ThreadedResource.new do
+ object
+ end
+ end
+
+ def teardown
+ resource.shutdown
+ end
+
+ def test_dispatch_completion
+ EM.run do
+ EM.add_timer(3) do
+ EM.stop
+ fail 'Resource dispatch timed out'
+ end
+ completion = resource.dispatch do |o|
+ o[:foo] = :bar
+ :foo
+ end
+ completion.callback do |result|
+ assert_equal :foo, result
+ EM.stop
+ end
+ completion.errback do |error|
+ EM.stop
+ fail "Unexpected error: #{error.message}"
+ end
+ end
+ assert_equal :bar, object[:foo]
+ end
+
+ def test_dispatch_failure
+ completion = resource.dispatch do |o|
+ raise 'boom'
+ end
+ completion.errback do |error|
+ assert_kind_of RuntimeError, error
+ assert_equal 'boom', error.message
+ end
+ end
+
+ def test_dispatch_threading
+ main = Thread.current
+ resource.dispatch do |o|
+ o[:dispatch_thread] = Thread.current
+ end
+ assert_not_equal main, object[:dispatch_thread]
+ end
+
+ def test_shutdown
+ # This test should get improved sometime. The method returning thread is
+ # NOT an api that will be maintained.
+ assert !resource.shutdown.alive?
+ end
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_tick_loop.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_tick_loop.rb
new file mode 100644
index 0000000..39d86b5
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_tick_loop.rb
@@ -0,0 +1,59 @@
+require "test/unit"
+require 'em_test_helper'
+
+class TestEmTickLoop < Test::Unit::TestCase
+ def test_em_tick_loop
+ i = 0
+ EM.tick_loop { i += 1; EM.stop if i == 10 }
+ EM.run { EM.add_timer(1) { EM.stop } }
+ assert_equal i, 10
+ end
+
+ def test_tick_loop_on_stop
+ t = nil
+ tick_loop = EM.tick_loop { :stop }
+ tick_loop.on_stop { t = true }
+ EM.run { EM.next_tick { EM.stop } }
+ assert t
+ end
+
+ def test_start_twice
+ i = 0
+ s = 0
+ tick_loop = EM.tick_loop { i += 1; :stop }
+ tick_loop.on_stop { s += 1; EM.stop }
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal 1, i
+ assert_equal 1, s
+ tick_loop.start
+ EM.run { EM.next_tick { EM.stop } }
+ assert_equal 2, i
+ assert_equal 1, s # stop callbacks are only called once
+ end
+
+ def test_stop
+ i, s = 0, 0
+ tick_loop = EM.tick_loop { i += 1 }
+ tick_loop.on_stop { s += 1 }
+ EM.run { EM.next_tick { tick_loop.stop; EM.next_tick { EM.stop } } }
+ assert tick_loop.stopped?
+ assert_equal 1, i
+ assert_equal 1, s
+ end
+
+ def test_immediate_stops
+ s = 0
+ tick_loop = EM::TickLoop.new { }
+ tick_loop.on_stop { s += 1 }
+ tick_loop.on_stop { s += 1 }
+ assert_equal 2, s
+ end
+
+ def test_stopped
+ tick_loop = EM::TickLoop.new { }
+ assert tick_loop.stopped?
+ tick_loop.start
+ assert !tick_loop.stopped?
+ end
+
+end \ No newline at end of file
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_timers.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_timers.rb
new file mode 100644
index 0000000..88b3b78
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_timers.rb
@@ -0,0 +1,130 @@
+require 'em_test_helper'
+
+class TestTimers < Test::Unit::TestCase
+
+ def test_timer_with_block
+ x = false
+ EM.run {
+ EM::Timer.new(0) {
+ x = true
+ EM.stop
+ }
+ }
+ assert x
+ end
+
+ def test_timer_with_proc
+ x = false
+ EM.run {
+ EM::Timer.new(0, proc {
+ x = true
+ EM.stop
+ })
+ }
+ assert x
+ end
+
+ def test_timer_cancel
+ assert_nothing_raised do
+ EM.run {
+ timer = EM::Timer.new(0.01) { flunk "Timer was not cancelled." }
+ timer.cancel
+
+ EM.add_timer(0.02) { EM.stop }
+ }
+ end
+ end
+
+ def test_periodic_timer
+ x = 0
+ EM.run {
+ EM::PeriodicTimer.new(0.01) do
+ x += 1
+ EM.stop if x == 4
+ end
+ }
+
+ assert_equal 4, x
+ end
+
+ def test_add_periodic_timer
+ x = 0
+ EM.run {
+ t = EM.add_periodic_timer(0.01) do
+ x += 1
+ EM.stop if x == 4
+ end
+ assert t.respond_to?(:cancel)
+ }
+ assert_equal 4, x
+ end
+
+ def test_periodic_timer_cancel
+ x = 0
+ EM.run {
+ pt = EM::PeriodicTimer.new(0.01) { x += 1 }
+ pt.cancel
+ EM::Timer.new(0.02) { EM.stop }
+ }
+ assert_equal 0, x
+ end
+
+ def test_add_periodic_timer_cancel
+ x = 0
+ EM.run {
+ pt = EM.add_periodic_timer(0.01) { x += 1 }
+ EM.cancel_timer(pt)
+ EM.add_timer(0.02) { EM.stop }
+ }
+ assert_equal 0, x
+ end
+
+ def test_periodic_timer_self_cancel
+ x = 0
+ EM.run {
+ pt = EM::PeriodicTimer.new(0) {
+ x += 1
+ if x == 4
+ pt.cancel
+ EM.stop
+ end
+ }
+ }
+ assert_equal 4, x
+ end
+
+ def test_oneshot_timer_large_future_value
+ large_value = 11948602000
+ EM.run {
+ EM.add_timer(large_value) { EM.stop }
+ EM.add_timer(0.02) { EM.stop }
+ }
+ end
+
+ # This test is only applicable to compiled versions of the reactor.
+ # Pure ruby and java versions have no built-in limit on the number of outstanding timers.
+ unless [:pure_ruby, :java].include? EM.library_type
+ def test_timer_change_max_outstanding
+ defaults = EM.get_max_timers
+ EM.set_max_timers(100)
+
+ one_hundred_one_timers = lambda do
+ 101.times { EM.add_timer(0.01) {} }
+ EM.stop
+ end
+
+ assert_raises(RuntimeError) do
+ EM.run( &one_hundred_one_timers )
+ end
+
+ EM.set_max_timers( 101 )
+
+ assert_nothing_raised do
+ EM.run( &one_hundred_one_timers )
+ end
+ ensure
+ EM.set_max_timers(defaults)
+ end
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ud.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ud.rb
new file mode 100644
index 0000000..3559756
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_ud.rb
@@ -0,0 +1,8 @@
+require 'em_test_helper'
+
+class TestUserDefinedEvents < Test::Unit::TestCase
+
+ def test_a
+ end
+
+end
diff --git a/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_unbind_reason.rb b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_unbind_reason.rb
new file mode 100644
index 0000000..ff9193e
--- /dev/null
+++ b/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/tests/test_unbind_reason.rb
@@ -0,0 +1,40 @@
+require 'em_test_helper'
+
+class TestUnbindReason < Test::Unit::TestCase
+
+ class StubConnection < EM::Connection
+ attr_reader :error
+ def unbind(reason = nil)
+ @error = reason
+ EM.stop
+ end
+ end
+
+ # RFC 5737 Address Blocks Reserved for Documentation
+ def test_connect_timeout
+ conn = nil
+ EM.run do
+ conn = EM.connect '192.0.2.0', 80, StubConnection
+ conn.pending_connect_timeout = 1
+ end
+ assert_equal Errno::ETIMEDOUT, conn.error
+ end
+
+ def test_connect_refused
+ pend('FIXME: this test is broken on Windows') if windows?
+ conn = nil
+ EM.run do
+ conn = EM.connect '127.0.0.1', 12388, StubConnection
+ end
+ assert_equal Errno::ECONNREFUSED, conn.error
+ end
+
+ def test_optional_argument
+ pend('FIXME: this test is broken on Windows') if windows?
+ conn = nil
+ EM.run do
+ conn = EM.connect '127.0.0.1', 12388, StubConnection
+ end
+ assert_equal Errno::ECONNREFUSED, conn.error
+ end
+end