summaryrefslogtreecommitdiff
path: root/vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/bundle/ruby/3.4.0/gems/eventmachine-1.2.7/ext')
-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
23 files changed, 10890 insertions, 0 deletions
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_
+