summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/webrick/utils.rb
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/webrick/utils.rb
Fresh start
Diffstat (limited to 'jni/ruby/lib/webrick/utils.rb')
-rw-r--r--jni/ruby/lib/webrick/utils.rb230
1 files changed, 230 insertions, 0 deletions
diff --git a/jni/ruby/lib/webrick/utils.rb b/jni/ruby/lib/webrick/utils.rb
new file mode 100644
index 0000000..606ede5
--- /dev/null
+++ b/jni/ruby/lib/webrick/utils.rb
@@ -0,0 +1,230 @@
+#
+# utils.rb -- Miscellaneous utilities
+#
+# Author: IPR -- Internet Programming with Ruby -- writers
+# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
+# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
+# reserved.
+#
+# $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
+
+require 'socket'
+require 'fcntl'
+require 'etc'
+
+module WEBrick
+ module Utils
+ ##
+ # Sets IO operations on +io+ to be non-blocking
+ def set_non_blocking(io)
+ flag = File::NONBLOCK
+ if defined?(Fcntl::F_GETFL)
+ flag |= io.fcntl(Fcntl::F_GETFL)
+ end
+ io.fcntl(Fcntl::F_SETFL, flag)
+ end
+ module_function :set_non_blocking
+
+ ##
+ # Sets the close on exec flag for +io+
+ def set_close_on_exec(io)
+ if defined?(Fcntl::FD_CLOEXEC)
+ io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
+ end
+ end
+ module_function :set_close_on_exec
+
+ ##
+ # Changes the process's uid and gid to the ones of +user+
+ def su(user)
+ if pw = Etc.getpwnam(user)
+ Process::initgroups(user, pw.gid)
+ Process::Sys::setgid(pw.gid)
+ Process::Sys::setuid(pw.uid)
+ else
+ warn("WEBrick::Utils::su doesn't work on this platform")
+ end
+ end
+ module_function :su
+
+ ##
+ # The server hostname
+ def getservername
+ host = Socket::gethostname
+ begin
+ Socket::gethostbyname(host)[0]
+ rescue
+ host
+ end
+ end
+ module_function :getservername
+
+ ##
+ # Creates TCP server sockets bound to +address+:+port+ and returns them.
+ #
+ # It will create IPV4 and IPV6 sockets on all interfaces.
+ def create_listeners(address, port, logger=nil)
+ unless port
+ raise ArgumentError, "must specify port"
+ end
+ sockets = Socket.tcp_server_sockets(address, port)
+ sockets = sockets.map {|s|
+ s.autoclose = false
+ ts = TCPServer.for_fd(s.fileno)
+ s.close
+ ts
+ }
+ return sockets
+ end
+ module_function :create_listeners
+
+ ##
+ # Characters used to generate random strings
+ RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
+ "0123456789" +
+ "abcdefghijklmnopqrstuvwxyz"
+
+ ##
+ # Generates a random string of length +len+
+ def random_string(len)
+ rand_max = RAND_CHARS.bytesize
+ ret = ""
+ len.times{ ret << RAND_CHARS[rand(rand_max)] }
+ ret
+ end
+ module_function :random_string
+
+ ###########
+
+ require "thread"
+ require "timeout"
+ require "singleton"
+
+ ##
+ # Class used to manage timeout handlers across multiple threads.
+ #
+ # Timeout handlers should be managed by using the class methods which are
+ # synchronized.
+ #
+ # id = TimeoutHandler.register(10, Timeout::Error)
+ # begin
+ # sleep 20
+ # puts 'foo'
+ # ensure
+ # TimeoutHandler.cancel(id)
+ # end
+ #
+ # will raise Timeout::Error
+ #
+ # id = TimeoutHandler.register(10, Timeout::Error)
+ # begin
+ # sleep 5
+ # puts 'foo'
+ # ensure
+ # TimeoutHandler.cancel(id)
+ # end
+ #
+ # will print 'foo'
+ #
+ class TimeoutHandler
+ include Singleton
+
+ class Thread < ::Thread; end
+
+ ##
+ # Mutex used to synchronize access across threads
+ TimeoutMutex = Mutex.new # :nodoc:
+
+ ##
+ # Registers a new timeout handler
+ #
+ # +time+:: Timeout in seconds
+ # +exception+:: Exception to raise when timeout elapsed
+ def TimeoutHandler.register(seconds, exception)
+ TimeoutMutex.synchronize{
+ instance.register(Thread.current, Time.now + seconds, exception)
+ }
+ end
+
+ ##
+ # Cancels the timeout handler +id+
+ def TimeoutHandler.cancel(id)
+ TimeoutMutex.synchronize{
+ instance.cancel(Thread.current, id)
+ }
+ end
+
+ ##
+ # Creates a new TimeoutHandler. You should use ::register and ::cancel
+ # instead of creating the timeout handler directly.
+ def initialize
+ @timeout_info = Hash.new
+ Thread.start{
+ while true
+ now = Time.now
+ @timeout_info.keys.each{|thread|
+ ary = @timeout_info[thread]
+ next unless ary
+ ary.dup.each{|info|
+ time, exception = *info
+ interrupt(thread, info.object_id, exception) if time < now
+ }
+ }
+ sleep 0.5
+ end
+ }
+ end
+
+ ##
+ # Interrupts the timeout handler +id+ and raises +exception+
+ def interrupt(thread, id, exception)
+ TimeoutMutex.synchronize{
+ if cancel(thread, id) && thread.alive?
+ thread.raise(exception, "execution timeout")
+ end
+ }
+ end
+
+ ##
+ # Registers a new timeout handler
+ #
+ # +time+:: Timeout in seconds
+ # +exception+:: Exception to raise when timeout elapsed
+ def register(thread, time, exception)
+ @timeout_info[thread] ||= Array.new
+ @timeout_info[thread] << [time, exception]
+ return @timeout_info[thread].last.object_id
+ end
+
+ ##
+ # Cancels the timeout handler +id+
+ def cancel(thread, id)
+ if ary = @timeout_info[thread]
+ ary.delete_if{|info| info.object_id == id }
+ if ary.empty?
+ @timeout_info.delete(thread)
+ end
+ return true
+ end
+ return false
+ end
+ end
+
+ ##
+ # Executes the passed block and raises +exception+ if execution takes more
+ # than +seconds+.
+ #
+ # If +seconds+ is zero or nil, simply executes the block
+ def timeout(seconds, exception=Timeout::Error)
+ return yield if seconds.nil? or seconds.zero?
+ # raise ThreadError, "timeout within critical session" if Thread.critical
+ id = TimeoutHandler.register(seconds, exception)
+ begin
+ yield(seconds)
+ ensure
+ TimeoutHandler.cancel(id)
+ end
+ end
+ module_function :timeout
+ end
+end