diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /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.rb | 230 |
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 |