webrick 1.3.1 → 1.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of webrick might be problematic. Click here for more details.

Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/webrick.rb +6 -6
  3. data/lib/webrick/accesslog.rb +9 -1
  4. data/lib/webrick/cgi.rb +51 -2
  5. data/lib/webrick/compat.rb +2 -1
  6. data/lib/webrick/config.rb +42 -5
  7. data/lib/webrick/cookie.rb +68 -6
  8. data/lib/webrick/htmlutils.rb +4 -2
  9. data/lib/webrick/httpauth.rb +1 -0
  10. data/lib/webrick/httpauth/authenticator.rb +13 -8
  11. data/lib/webrick/httpauth/basicauth.rb +3 -3
  12. data/lib/webrick/httpauth/digestauth.rb +25 -9
  13. data/lib/webrick/httpauth/htdigest.rb +8 -4
  14. data/lib/webrick/httpauth/htgroup.rb +1 -0
  15. data/lib/webrick/httpauth/htpasswd.rb +7 -3
  16. data/lib/webrick/httpauth/userdb.rb +1 -0
  17. data/lib/webrick/httpproxy.rb +47 -14
  18. data/lib/webrick/httprequest.rb +142 -16
  19. data/lib/webrick/httpresponse.rb +96 -24
  20. data/lib/webrick/https.rb +24 -1
  21. data/lib/webrick/httpserver.rb +20 -4
  22. data/lib/webrick/httpservlet.rb +1 -0
  23. data/lib/webrick/httpservlet/abstract.rb +2 -1
  24. data/lib/webrick/httpservlet/cgi_runner.rb +1 -0
  25. data/lib/webrick/httpservlet/cgihandler.rb +19 -5
  26. data/lib/webrick/httpservlet/erbhandler.rb +2 -1
  27. data/lib/webrick/httpservlet/filehandler.rb +87 -34
  28. data/lib/webrick/httpservlet/prochandler.rb +14 -0
  29. data/lib/webrick/httpstatus.rb +24 -10
  30. data/lib/webrick/httputils.rb +129 -13
  31. data/lib/webrick/httpversion.rb +28 -1
  32. data/lib/webrick/log.rb +22 -2
  33. data/lib/webrick/server.rb +203 -60
  34. data/lib/webrick/ssl.rb +80 -5
  35. data/lib/webrick/utils.rb +97 -67
  36. data/lib/webrick/version.rb +6 -1
  37. metadata +59 -69
  38. data/README.txt +0 -21
  39. data/sample/webrick/demo-app.rb +0 -66
  40. data/sample/webrick/demo-multipart.cgi +0 -12
  41. data/sample/webrick/demo-servlet.rb +0 -6
  42. data/sample/webrick/demo-urlencoded.cgi +0 -12
  43. data/sample/webrick/hello.cgi +0 -11
  44. data/sample/webrick/hello.rb +0 -8
  45. data/sample/webrick/httpd.rb +0 -23
  46. data/sample/webrick/httpproxy.rb +0 -25
  47. data/sample/webrick/httpsd.rb +0 -33
  48. data/test/openssl/utils.rb +0 -313
  49. data/test/ruby/envutil.rb +0 -208
  50. data/test/webrick/test_cgi.rb +0 -134
  51. data/test/webrick/test_cookie.rb +0 -131
  52. data/test/webrick/test_filehandler.rb +0 -285
  53. data/test/webrick/test_httpauth.rb +0 -167
  54. data/test/webrick/test_httpproxy.rb +0 -282
  55. data/test/webrick/test_httprequest.rb +0 -411
  56. data/test/webrick/test_httpresponse.rb +0 -49
  57. data/test/webrick/test_httpserver.rb +0 -305
  58. data/test/webrick/test_httputils.rb +0 -96
  59. data/test/webrick/test_httpversion.rb +0 -40
  60. data/test/webrick/test_server.rb +0 -67
  61. data/test/webrick/test_utils.rb +0 -64
  62. data/test/webrick/utils.rb +0 -58
  63. data/test/webrick/webrick.cgi +0 -36
  64. data/test/webrick/webrick_long_filename.cgi +0 -36
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # HTTPVersion.rb -- presentation of HTTP version
3
4
  #
@@ -8,15 +9,33 @@
8
9
  # $IPR: httpversion.rb,v 1.5 2002/09/21 12:23:37 gotoyuzo Exp $
9
10
 
10
11
  module WEBrick
12
+
13
+ ##
14
+ # Represents an HTTP protocol version
15
+
11
16
  class HTTPVersion
12
17
  include Comparable
13
18
 
14
- attr_accessor :major, :minor
19
+ ##
20
+ # The major protocol version number
21
+
22
+ attr_accessor :major
23
+
24
+ ##
25
+ # The minor protocol version number
26
+
27
+ attr_accessor :minor
28
+
29
+ ##
30
+ # Converts +version+ into an HTTPVersion
15
31
 
16
32
  def self.convert(version)
17
33
  version.is_a?(self) ? version : new(version)
18
34
  end
19
35
 
36
+ ##
37
+ # Creates a new HTTPVersion from +version+.
38
+
20
39
  def initialize(version)
21
40
  case version
22
41
  when HTTPVersion
@@ -32,6 +51,10 @@ module WEBrick
32
51
  end
33
52
  end
34
53
 
54
+ ##
55
+ # Compares this version with +other+ according to the HTTP specification
56
+ # rules.
57
+
35
58
  def <=>(other)
36
59
  unless other.is_a?(self.class)
37
60
  other = self.class.new(other)
@@ -42,6 +65,10 @@ module WEBrick
42
65
  return ret
43
66
  end
44
67
 
68
+ ##
69
+ # The HTTP version as show in the HTTP request and response. For example,
70
+ # "1.1"
71
+
45
72
  def to_s
46
73
  format("%d.%d", @major, @minor)
47
74
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # log.rb -- Log Class
3
4
  #
@@ -14,8 +15,27 @@ module WEBrick
14
15
  # A generic logging class
15
16
 
16
17
  class BasicLog
17
- # log-level constants
18
- FATAL, ERROR, WARN, INFO, DEBUG = 1, 2, 3, 4, 5
18
+
19
+ # Fatal log level which indicates a server crash
20
+
21
+ FATAL = 1
22
+
23
+ # Error log level which indicates a recoverable error
24
+
25
+ ERROR = 2
26
+
27
+ # Warning log level which indicates a possible problem
28
+
29
+ WARN = 3
30
+
31
+ # Information log level which indicates possibly useful information
32
+
33
+ INFO = 4
34
+
35
+ # Debugging error level for messages used in server development or
36
+ # debugging
37
+
38
+ DEBUG = 5
19
39
 
20
40
  # log-level, messages above this level will be logged
21
41
  attr_accessor :level
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # server.rb -- GenericServer Class
3
4
  #
@@ -15,9 +16,19 @@ require 'webrick/log'
15
16
 
16
17
  module WEBrick
17
18
 
19
+ ##
20
+ # Server error exception
21
+
18
22
  class ServerError < StandardError; end
19
23
 
24
+ ##
25
+ # Base server class
26
+
20
27
  class SimpleServer
28
+
29
+ ##
30
+ # A SimpleServer only yields when you start it
31
+
21
32
  def SimpleServer.start
22
33
  yield
23
34
  end
@@ -33,20 +44,47 @@ module WEBrick
33
44
  # block, if given.
34
45
 
35
46
  def Daemon.start
36
- exit!(0) if fork
37
- Process::setsid
38
- exit!(0) if fork
39
- Dir::chdir("/")
40
- File::umask(0)
41
- STDIN.reopen("/dev/null")
42
- STDOUT.reopen("/dev/null", "w")
43
- STDERR.reopen("/dev/null", "w")
47
+ Process.daemon
48
+ File.umask(0)
44
49
  yield if block_given?
45
50
  end
46
51
  end
47
52
 
53
+ ##
54
+ # Base TCP server class. You must subclass GenericServer and provide a #run
55
+ # method.
56
+
48
57
  class GenericServer
49
- attr_reader :status, :config, :logger, :tokens, :listeners
58
+
59
+ ##
60
+ # The server status. One of :Stop, :Running or :Shutdown
61
+
62
+ attr_reader :status
63
+
64
+ ##
65
+ # The server configuration
66
+
67
+ attr_reader :config
68
+
69
+ ##
70
+ # The server logger. This is independent from the HTTP access log.
71
+
72
+ attr_reader :logger
73
+
74
+ ##
75
+ # Tokens control the number of outstanding clients. The
76
+ # <code>:MaxClients</code> configuration sets this.
77
+
78
+ attr_reader :tokens
79
+
80
+ ##
81
+ # Sockets listening for connections.
82
+
83
+ attr_reader :listeners
84
+
85
+ ##
86
+ # Creates a new generic server from +config+. The default configuration
87
+ # comes from +default+.
50
88
 
51
89
  def initialize(config={}, default=Config::General)
52
90
  @config = default.dup.update(config)
@@ -54,7 +92,7 @@ module WEBrick
54
92
  @config[:Logger] ||= Log::new
55
93
  @logger = @config[:Logger]
56
94
 
57
- @tokens = SizedQueue.new(@config[:MaxClients])
95
+ @tokens = Thread::SizedQueue.new(@config[:MaxClients])
58
96
  @config[:MaxClients].times{ @tokens.push(nil) }
59
97
 
60
98
  webrickv = WEBrick::VERSION
@@ -63,6 +101,7 @@ module WEBrick
63
101
  @logger.info("ruby #{rubyv}")
64
102
 
65
103
  @listeners = []
104
+ @shutdown_pipe = nil
66
105
  unless @config[:DoNotListen]
67
106
  if @config[:Listen]
68
107
  warn(":Listen option is deprecated; use GenericServer#listen")
@@ -74,108 +113,168 @@ module WEBrick
74
113
  end
75
114
  end
76
115
 
116
+ ##
117
+ # Retrieves +key+ from the configuration
118
+
77
119
  def [](key)
78
120
  @config[key]
79
121
  end
80
122
 
123
+ ##
124
+ # Adds listeners from +address+ and +port+ to the server. See
125
+ # WEBrick::Utils::create_listeners for details.
126
+
81
127
  def listen(address, port)
82
- @listeners += Utils::create_listeners(address, port, @logger)
128
+ @listeners += Utils::create_listeners(address, port)
83
129
  end
84
130
 
131
+ ##
132
+ # Starts the server and runs the +block+ for each connection. This method
133
+ # does not return until the server is stopped from a signal handler or
134
+ # another thread using #stop or #shutdown.
135
+ #
136
+ # If the block raises a subclass of StandardError the exception is logged
137
+ # and ignored. If an IOError or Errno::EBADF exception is raised the
138
+ # exception is ignored. If an Exception subclass is raised the exception
139
+ # is logged and re-raised which stops the server.
140
+ #
141
+ # To completely shut down a server call #shutdown from ensure:
142
+ #
143
+ # server = WEBrick::GenericServer.new
144
+ # # or WEBrick::HTTPServer.new
145
+ #
146
+ # begin
147
+ # server.start
148
+ # ensure
149
+ # server.shutdown
150
+ # end
151
+
85
152
  def start(&block)
86
153
  raise ServerError, "already started." if @status != :Stop
87
154
  server_type = @config[:ServerType] || SimpleServer
88
155
 
156
+ setup_shutdown_pipe
157
+
89
158
  server_type.start{
90
159
  @logger.info \
91
160
  "#{self.class}#start: pid=#{$$} port=#{@config[:Port]}"
92
161
  call_callback(:StartCallback)
93
162
 
163
+ shutdown_pipe = @shutdown_pipe
164
+
94
165
  thgroup = ThreadGroup.new
95
166
  @status = :Running
96
- while @status == :Running
97
- begin
98
- if svrs = IO.select(@listeners, nil, nil, 2.0)
99
- svrs[0].each{|svr|
100
- @tokens.pop # blocks while no token is there.
101
- if sock = accept_client(svr)
102
- sock.do_not_reverse_lookup = config[:DoNotReverseLookup]
103
- th = start_thread(sock, &block)
104
- th[:WEBrickThread] = true
105
- thgroup.add(th)
106
- else
107
- @tokens.push(nil)
167
+ begin
168
+ while @status == :Running
169
+ begin
170
+ sp = shutdown_pipe[0]
171
+ if svrs = IO.select([sp, *@listeners], nil, nil, 2.0)
172
+ if svrs[0].include? sp
173
+ # swallow shutdown pipe
174
+ buf = String.new
175
+ nil while String ===
176
+ sp.read_nonblock([sp.nread, 8].max, buf, exception: false)
177
+ break
108
178
  end
109
- }
179
+ svrs[0].each{|svr|
180
+ @tokens.pop # blocks while no token is there.
181
+ if sock = accept_client(svr)
182
+ unless config[:DoNotReverseLookup].nil?
183
+ sock.do_not_reverse_lookup = !!config[:DoNotReverseLookup]
184
+ end
185
+ th = start_thread(sock, &block)
186
+ th[:WEBrickThread] = true
187
+ thgroup.add(th)
188
+ else
189
+ @tokens.push(nil)
190
+ end
191
+ }
192
+ end
193
+ rescue Errno::EBADF, Errno::ENOTSOCK, IOError => ex
194
+ # if the listening socket was closed in GenericServer#shutdown,
195
+ # IO::select raise it.
196
+ rescue StandardError => ex
197
+ msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
198
+ @logger.error msg
199
+ rescue Exception => ex
200
+ @logger.fatal ex
201
+ raise
110
202
  end
111
- rescue Errno::EBADF, IOError => ex
112
- # if the listening socket was closed in GenericServer#shutdown,
113
- # IO::select raise it.
114
- rescue Exception => ex
115
- msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
116
- @logger.error msg
117
203
  end
204
+ ensure
205
+ cleanup_shutdown_pipe(shutdown_pipe)
206
+ cleanup_listener
207
+ @status = :Shutdown
208
+ @logger.info "going to shutdown ..."
209
+ thgroup.list.each{|th| th.join if th[:WEBrickThread] }
210
+ call_callback(:StopCallback)
211
+ @logger.info "#{self.class}#start done."
212
+ @status = :Stop
118
213
  end
119
-
120
- @logger.info "going to shutdown ..."
121
- thgroup.list.each{|th| th.join if th[:WEBrickThread] }
122
- call_callback(:StopCallback)
123
- @logger.info "#{self.class}#start done."
124
- @status = :Stop
125
214
  }
126
215
  end
127
216
 
217
+ ##
218
+ # Stops the server from accepting new connections.
219
+
128
220
  def stop
129
221
  if @status == :Running
130
222
  @status = :Shutdown
131
223
  end
224
+
225
+ alarm_shutdown_pipe {|f| f.write_nonblock("\0")}
132
226
  end
133
227
 
228
+ ##
229
+ # Shuts down the server and all listening sockets. New listeners must be
230
+ # provided to restart the server.
231
+
134
232
  def shutdown
135
233
  stop
136
- @listeners.each{|s|
137
- if @logger.debug?
138
- addr = s.addr
139
- @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
140
- end
141
- begin
142
- s.shutdown
143
- rescue Errno::ENOTCONN
144
- # when `Errno::ENOTCONN: Socket is not connected' on some platforms,
145
- # call #close instead of #shutdown.
146
- # (ignore @config[:ShutdownSocketWithoutClose])
147
- s.close
148
- else
149
- unless @config[:ShutdownSocketWithoutClose]
150
- s.close
151
- end
152
- end
153
- }
154
- @listeners.clear
234
+
235
+ alarm_shutdown_pipe(&:close)
155
236
  end
156
237
 
238
+ ##
239
+ # You must subclass GenericServer and implement \#run which accepts a TCP
240
+ # client socket
241
+
157
242
  def run(sock)
158
243
  @logger.fatal "run() must be provided by user."
159
244
  end
160
245
 
161
246
  private
162
247
 
248
+ # :stopdoc:
249
+
250
+ ##
251
+ # Accepts a TCP client socket from the TCP server socket +svr+ and returns
252
+ # the client socket.
253
+
163
254
  def accept_client(svr)
164
255
  sock = nil
165
256
  begin
166
257
  sock = svr.accept
167
258
  sock.sync = true
168
259
  Utils::set_non_blocking(sock)
169
- Utils::set_close_on_exec(sock)
170
260
  rescue Errno::ECONNRESET, Errno::ECONNABORTED,
171
- Errno::EPROTO, Errno::EINVAL => ex
172
- rescue Exception => ex
261
+ Errno::EPROTO, Errno::EINVAL
262
+ rescue StandardError => ex
173
263
  msg = "#{ex.class}: #{ex.message}\n\t#{ex.backtrace[0]}"
174
264
  @logger.error msg
175
265
  end
176
266
  return sock
177
267
  end
178
268
 
269
+ ##
270
+ # Starts a server thread for the client socket +sock+ that runs the given
271
+ # +block+.
272
+ #
273
+ # Sets the socket to the <code>:WEBrickSocket</code> thread local variable
274
+ # in the thread.
275
+ #
276
+ # If any errors occur in the block they are logged and handled.
277
+
179
278
  def start_thread(sock, &block)
180
279
  Thread.start{
181
280
  begin
@@ -209,10 +308,54 @@ module WEBrick
209
308
  }
210
309
  end
211
310
 
311
+ ##
312
+ # Calls the callback +callback_name+ from the configuration with +args+
313
+
212
314
  def call_callback(callback_name, *args)
213
- if cb = @config[callback_name]
214
- cb.call(*args)
315
+ @config[callback_name]&.call(*args)
316
+ end
317
+
318
+ def setup_shutdown_pipe
319
+ return @shutdown_pipe ||= IO.pipe
320
+ end
321
+
322
+ def cleanup_shutdown_pipe(shutdown_pipe)
323
+ @shutdown_pipe = nil
324
+ shutdown_pipe&.each(&:close)
325
+ end
326
+
327
+ def alarm_shutdown_pipe
328
+ _, pipe = @shutdown_pipe # another thread may modify @shutdown_pipe.
329
+ if pipe
330
+ if !pipe.closed?
331
+ begin
332
+ yield pipe
333
+ rescue IOError # closed by another thread.
334
+ end
335
+ end
215
336
  end
216
337
  end
338
+
339
+ def cleanup_listener
340
+ @listeners.each{|s|
341
+ if @logger.debug?
342
+ addr = s.addr
343
+ @logger.debug("close TCPSocket(#{addr[2]}, #{addr[1]})")
344
+ end
345
+ begin
346
+ s.shutdown
347
+ rescue Errno::ENOTCONN
348
+ # when `Errno::ENOTCONN: Socket is not connected' on some platforms,
349
+ # call #close instead of #shutdown.
350
+ # (ignore @config[:ShutdownSocketWithoutClose])
351
+ s.close
352
+ else
353
+ unless @config[:ShutdownSocketWithoutClose]
354
+ s.close
355
+ end
356
+ end
357
+ }
358
+ @listeners.clear
359
+ end
217
360
  end # end of GenericServer
218
361
  end