PageRenderTime 51ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ruby/1.9/webrick/utils.rb

http://github.com/jruby/jruby
Ruby | 241 lines | 222 code | 4 blank | 15 comment | 3 complexity | b5e5b09132843bcb3af74f552ba28fd6 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, GPL-2.0, JSON, LGPL-2.1
  1. #
  2. # utils.rb -- Miscellaneous utilities
  3. #
  4. # Author: IPR -- Internet Programming with Ruby -- writers
  5. # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
  6. # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
  7. # reserved.
  8. #
  9. # $IPR: utils.rb,v 1.10 2003/02/16 22:22:54 gotoyuzo Exp $
  10. require 'socket'
  11. require 'fcntl'
  12. begin
  13. require 'etc'
  14. rescue LoadError
  15. nil
  16. end
  17. module WEBrick
  18. module Utils
  19. ##
  20. # Sets IO operations on +io+ to be non-blocking
  21. def set_non_blocking(io)
  22. flag = File::NONBLOCK
  23. if defined?(Fcntl::F_GETFL)
  24. flag |= io.fcntl(Fcntl::F_GETFL)
  25. end
  26. io.fcntl(Fcntl::F_SETFL, flag)
  27. end
  28. module_function :set_non_blocking
  29. ##
  30. # Sets the close on exec flag for +io+
  31. def set_close_on_exec(io)
  32. if defined?(Fcntl::FD_CLOEXEC)
  33. io.fcntl(Fcntl::FD_CLOEXEC, 1)
  34. end
  35. end
  36. module_function :set_close_on_exec
  37. ##
  38. # Changes the process's uid and gid to the ones of +user+
  39. def su(user)
  40. if defined?(Etc)
  41. pw = Etc.getpwnam(user)
  42. Process::initgroups(user, pw.gid)
  43. Process::Sys::setgid(pw.gid)
  44. Process::Sys::setuid(pw.uid)
  45. else
  46. warn("WEBrick::Utils::su doesn't work on this platform")
  47. end
  48. end
  49. module_function :su
  50. ##
  51. # The server hostname
  52. def getservername
  53. host = Socket::gethostname
  54. begin
  55. Socket::gethostbyname(host)[0]
  56. rescue
  57. host
  58. end
  59. end
  60. module_function :getservername
  61. ##
  62. # Creates TCP server sockets bound to +address+:+port+ and returns them.
  63. #
  64. # It will create IPV4 and IPV6 sockets on all interfaces.
  65. def create_listeners(address, port, logger=nil)
  66. unless port
  67. raise ArgumentError, "must specify port"
  68. end
  69. res = Socket::getaddrinfo(address, port,
  70. Socket::AF_UNSPEC, # address family
  71. Socket::SOCK_STREAM, # socket type
  72. 0, # protocol
  73. Socket::AI_PASSIVE) # flag
  74. last_error = nil
  75. sockets = []
  76. res.each{|ai|
  77. begin
  78. logger.debug("TCPServer.new(#{ai[3]}, #{port})") if logger
  79. sock = TCPServer.new(ai[3], port)
  80. port = sock.addr[1] if port == 0
  81. Utils::set_close_on_exec(sock)
  82. sockets << sock
  83. rescue => ex
  84. logger.warn("TCPServer Error: #{ex}") if logger
  85. last_error = ex
  86. end
  87. }
  88. raise last_error if sockets.empty?
  89. return sockets
  90. end
  91. module_function :create_listeners
  92. ##
  93. # Characters used to generate random strings
  94. RAND_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
  95. "0123456789" +
  96. "abcdefghijklmnopqrstuvwxyz"
  97. ##
  98. # Generates a random string of length +len+
  99. def random_string(len)
  100. rand_max = RAND_CHARS.bytesize
  101. ret = ""
  102. len.times{ ret << RAND_CHARS[rand(rand_max)] }
  103. ret
  104. end
  105. module_function :random_string
  106. ###########
  107. require "thread"
  108. require "timeout"
  109. require "singleton"
  110. ##
  111. # Class used to manage timeout handlers across multiple threads.
  112. #
  113. # Timeout handlers should be managed by using the class methods which are
  114. # synchronized.
  115. #
  116. # id = TimeoutHandler.register(10, Timeout::Error)
  117. # begin
  118. # sleep 20
  119. # puts 'foo'
  120. # ensure
  121. # TimeoutHandler.cancel(id)
  122. # end
  123. #
  124. # will raise Timeout::Error
  125. #
  126. # id = TimeoutHandler.register(10, Timeout::Error)
  127. # begin
  128. # sleep 5
  129. # puts 'foo'
  130. # ensure
  131. # TimeoutHandler.cancel(id)
  132. # end
  133. #
  134. # will print 'foo'
  135. #
  136. class TimeoutHandler
  137. include Singleton
  138. ##
  139. # Mutex used to synchronize access across threads
  140. TimeoutMutex = Mutex.new # :nodoc:
  141. ##
  142. # Registers a new timeout handler
  143. #
  144. # +time+:: Timeout in seconds
  145. # +exception+:: Exception to raise when timeout elapsed
  146. def TimeoutHandler.register(seconds, exception)
  147. TimeoutMutex.synchronize{
  148. instance.register(Thread.current, Time.now + seconds, exception)
  149. }
  150. end
  151. ##
  152. # Cancels the timeout handler +id+
  153. def TimeoutHandler.cancel(id)
  154. TimeoutMutex.synchronize{
  155. instance.cancel(Thread.current, id)
  156. }
  157. end
  158. def initialize
  159. @timeout_info = Hash.new
  160. Thread.start{
  161. while true
  162. now = Time.now
  163. @timeout_info.each{|thread, ary|
  164. ary.dup.each{|info|
  165. time, exception = *info
  166. interrupt(thread, info.object_id, exception) if time < now
  167. }
  168. }
  169. sleep 0.5
  170. end
  171. }
  172. end
  173. ##
  174. # Interrupts the timeout handler +id+ and raises +exception+
  175. def interrupt(thread, id, exception)
  176. TimeoutMutex.synchronize{
  177. if cancel(thread, id) && thread.alive?
  178. thread.raise(exception, "execution timeout")
  179. end
  180. }
  181. end
  182. ##
  183. # Registers a new timeout handler
  184. #
  185. # +time+:: Timeout in seconds
  186. # +exception+:: Exception to raise when timeout elapsed
  187. def register(thread, time, exception)
  188. @timeout_info[thread] ||= Array.new
  189. @timeout_info[thread] << [time, exception]
  190. return @timeout_info[thread].last.object_id
  191. end
  192. ##
  193. # Cancels the timeout handler +id+
  194. def cancel(thread, id)
  195. if ary = @timeout_info[thread]
  196. ary.delete_if{|info| info.object_id == id }
  197. if ary.empty?
  198. @timeout_info.delete(thread)
  199. end
  200. return true
  201. end
  202. return false
  203. end
  204. end
  205. ##
  206. # Executes the passed block and raises +exception+ if execution takes more
  207. # than +seconds+.
  208. #
  209. # If +seconds+ is zero or nil, simply executes the block
  210. def timeout(seconds, exception=Timeout::Error)
  211. return yield if seconds.nil? or seconds.zero?
  212. # raise ThreadError, "timeout within critical session" if Thread.critical
  213. id = TimeoutHandler.register(seconds, exception)
  214. begin
  215. yield(seconds)
  216. ensure
  217. TimeoutHandler.cancel(id)
  218. end
  219. end
  220. module_function :timeout
  221. end
  222. end