Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,228 @@
# frozen_string_literal: true
module Ethon
class Multi # :nodoc
# This module contains logic to run a multi.
module Operations
STARTED_MULTI = "ETHON: started MULTI"
PERFORMED_MULTI = "ETHON: performed MULTI"
# Return the multi handle. Inititialize multi handle,
# in case it didn't happened already.
#
# @example Return multi handle.
# multi.handle
#
# @return [ FFI::Pointer ] The multi handle.
def handle
@handle ||= FFI::AutoPointer.new(Curl.multi_init, Curl.method(:multi_cleanup))
end
# Initialize variables.
#
# @example Initialize variables.
# multi.init_vars
#
# @return [ void ]
def init_vars
if @execution_mode == :perform
@timeout = ::FFI::MemoryPointer.new(:long)
@timeval = Curl::Timeval.new
@fd_read = Curl::FDSet.new
@fd_write = Curl::FDSet.new
@fd_excep = Curl::FDSet.new
@max_fd = ::FFI::MemoryPointer.new(:int)
elsif @execution_mode == :socket_action
@running_count_pointer = FFI::MemoryPointer.new(:int)
end
end
# Perform multi.
#
# @return [ nil ]
#
# @example Perform multi.
# multi.perform
def perform
ensure_execution_mode(:perform)
Ethon.logger.debug(STARTED_MULTI)
while ongoing?
run
timeout = get_timeout
next if timeout == 0
reset_fds
set_fds(timeout)
end
Ethon.logger.debug(PERFORMED_MULTI)
nil
end
# Prepare multi.
#
# @return [ nil ]
#
# @example Prepare multi.
# multi.prepare
#
# @deprecated It is no longer necessary to call prepare.
def prepare
Ethon.logger.warn(
"ETHON: It is no longer necessay to call "+
"Multi#prepare. Its going to be removed "+
"in future versions."
)
end
# Continue execution with an external IO loop.
#
# @example When no sockets are ready yet, or to begin.
# multi.socket_action
#
# @example When a socket is readable
# multi.socket_action(io_object, [:in])
#
# @example When a socket is readable and writable
# multi.socket_action(io_object, [:in, :out])
#
# @return [ Symbol ] The Curl.multi_socket_action return code.
def socket_action(io = nil, readiness = 0)
ensure_execution_mode(:socket_action)
fd = if io.nil?
::Ethon::Curl::SOCKET_TIMEOUT
elsif io.is_a?(Integer)
io
else
io.fileno
end
code = Curl.multi_socket_action(handle, fd, readiness, @running_count_pointer)
@running_count = @running_count_pointer.read_int
check
code
end
# Return whether the multi still contains requests or not.
#
# @example Return if ongoing.
# multi.ongoing?
#
# @return [ Boolean ] True if ongoing, else false.
def ongoing?
easy_handles.size > 0 || (!defined?(@running_count) || running_count > 0)
end
private
# Get timeout.
#
# @example Get timeout.
# multi.get_timeout
#
# @return [ Integer ] The timeout.
#
# @raise [ Ethon::Errors::MultiTimeout ] If getting the timeout fails.
def get_timeout
code = Curl.multi_timeout(handle, @timeout)
raise Errors::MultiTimeout.new(code) unless code == :ok
timeout = @timeout.read_long
timeout = 1 if timeout < 0
timeout
end
# Reset file describtors.
#
# @example Reset fds.
# multi.reset_fds
#
# @return [ void ]
def reset_fds
@fd_read.clear
@fd_write.clear
@fd_excep.clear
end
# Set fds.
#
# @example Set fds.
# multi.set_fds
#
# @return [ void ]
#
# @raise [ Ethon::Errors::MultiFdset ] If setting the file descriptors fails.
# @raise [ Ethon::Errors::Select ] If select fails.
def set_fds(timeout)
code = Curl.multi_fdset(handle, @fd_read, @fd_write, @fd_excep, @max_fd)
raise Errors::MultiFdset.new(code) unless code == :ok
max_fd = @max_fd.read_int
if max_fd == -1
sleep(0.001)
else
@timeval[:sec] = timeout / 1000
@timeval[:usec] = (timeout * 1000) % 1000000
loop do
code = Curl.select(max_fd + 1, @fd_read, @fd_write, @fd_excep, @timeval)
break unless code < 0 && ::FFI.errno == Errno::EINTR::Errno
end
raise Errors::Select.new(::FFI.errno) if code < 0
end
end
# Check.
#
# @example Check.
# multi.check
#
# @return [ void ]
def check
msgs_left = ::FFI::MemoryPointer.new(:int)
while true
msg = Curl.multi_info_read(handle, msgs_left)
break if msg.null?
next if msg[:code] != :done
easy = easy_handles.find{ |e| e.handle == msg[:easy_handle] }
easy.return_code = msg[:data][:code]
Ethon.logger.debug { "ETHON: performed #{easy.log_inspect}" }
delete(easy)
easy.complete
end
end
# Run.
#
# @example Run
# multi.run
#
# @return [ void ]
def run
running_count_pointer = FFI::MemoryPointer.new(:int)
begin code = trigger(running_count_pointer) end while code == :call_multi_perform
check
end
# Trigger.
#
# @example Trigger.
# multi.trigger
#
# @return [ Symbol ] The Curl.multi_perform return code.
def trigger(running_count_pointer)
code = Curl.multi_perform(handle, running_count_pointer)
@running_count = running_count_pointer.read_int
code
end
# Return number of running requests.
#
# @example Return count.
# multi.running_count
#
# @return [ Integer ] Number running requests.
def running_count
@running_count ||= nil
end
end
end
end
@@ -0,0 +1,117 @@
# frozen_string_literal: true
module Ethon
class Multi
# This module contains the logic and knowledge about the
# available options on multi.
module Options
# Sets max_total_connections option.
#
# @example Set max_total_connections option.
# multi.max_total_conections = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def max_total_connections=(value)
Curl.set_option(:max_total_connections, value_for(value, :int), handle, :multi)
end
# Sets maxconnects option.
#
# @example Set maxconnects option.
# multi.maxconnects = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def maxconnects=(value)
Curl.set_option(:maxconnects, value_for(value, :int), handle, :multi)
end
# Sets pipelining option.
#
# @example Set pipelining option.
# multi.pipelining = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def pipelining=(value)
Curl.set_option(:pipelining, value_for(value, :int), handle, :multi)
end
# Sets socketdata option.
#
# @example Set socketdata option.
# multi.socketdata = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def socketdata=(value)
Curl.set_option(:socketdata, value_for(value, :string), handle, :multi)
end
# Sets socketfunction option.
#
# @example Set socketfunction option.
# multi.socketfunction = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def socketfunction=(value)
Curl.set_option(:socketfunction, value_for(value, :string), handle, :multi)
end
# Sets timerdata option.
#
# @example Set timerdata option.
# multi.timerdata = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def timerdata=(value)
Curl.set_option(:timerdata, value_for(value, :string), handle, :multi)
end
# Sets timerfunction option.
#
# @example Set timerfunction option.
# multi.timerfunction = $value
#
# @param [ String ] value The value to set.
#
# @return [ void ]
def timerfunction=(value)
Curl.set_option(:timerfunction, value_for(value, :string), handle, :multi)
end
private
# Return the value to set to multi handle. It is converted with the help
# of bool_options, enum_options and int_options.
#
# @example Return casted the value.
# multi.value_for(:verbose)
#
# @return [ Object ] The casted value.
def value_for(value, type, option = nil)
return nil if value.nil?
if type == :bool
value ? 1 : 0
elsif type == :int
value.to_i
elsif value.is_a?(String)
Ethon::Easy::Util.escape_zero_byte(value)
else
value
end
end
end
end
end
@@ -0,0 +1,49 @@
# frozen_string_literal: true
module Ethon
class Multi
# This module provides the multi stack behaviour.
module Stack
# Return easy handles.
#
# @example Return easy handles.
# multi.easy_handles
#
# @return [ Array ] The easy handles.
def easy_handles
@easy_handles ||= []
end
# Add an easy to the stack.
#
# @example Add easy.
# multi.add(easy)
#
# @param [ Easy ] easy The easy to add.
#
# @raise [ Ethon::Errors::MultiAdd ] If adding an easy failed.
def add(easy)
return nil if easy_handles.include?(easy)
code = Curl.multi_add_handle(handle, easy.handle)
raise Errors::MultiAdd.new(code, easy) unless code == :ok
easy_handles << easy
end
# Delete an easy from stack.
#
# @example Delete easy from stack.
#
# @param [ Easy ] easy The easy to delete.
#
# @raise [ Ethon::Errors::MultiRemove ] If removing an easy failed.
def delete(easy)
if easy_handles.delete(easy)
code = Curl.multi_remove_handle(handle, easy.handle)
raise Errors::MultiRemove.new(code, handle) unless code == :ok
end
end
end
end
end