rubysl-webrick 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/rubysl/webrick.rb +2 -0
  9. data/lib/rubysl/webrick/version.rb +5 -0
  10. data/lib/rubysl/webrick/webrick.rb +29 -0
  11. data/lib/webrick.rb +1 -0
  12. data/lib/webrick/accesslog.rb +67 -0
  13. data/lib/webrick/cgi.rb +257 -0
  14. data/lib/webrick/compat.rb +15 -0
  15. data/lib/webrick/config.rb +97 -0
  16. data/lib/webrick/cookie.rb +110 -0
  17. data/lib/webrick/htmlutils.rb +25 -0
  18. data/lib/webrick/httpauth.rb +45 -0
  19. data/lib/webrick/httpauth/authenticator.rb +79 -0
  20. data/lib/webrick/httpauth/basicauth.rb +65 -0
  21. data/lib/webrick/httpauth/digestauth.rb +343 -0
  22. data/lib/webrick/httpauth/htdigest.rb +91 -0
  23. data/lib/webrick/httpauth/htgroup.rb +61 -0
  24. data/lib/webrick/httpauth/htpasswd.rb +83 -0
  25. data/lib/webrick/httpauth/userdb.rb +29 -0
  26. data/lib/webrick/httpproxy.rb +254 -0
  27. data/lib/webrick/httprequest.rb +365 -0
  28. data/lib/webrick/httpresponse.rb +327 -0
  29. data/lib/webrick/https.rb +63 -0
  30. data/lib/webrick/httpserver.rb +210 -0
  31. data/lib/webrick/httpservlet.rb +22 -0
  32. data/lib/webrick/httpservlet/abstract.rb +71 -0
  33. data/lib/webrick/httpservlet/cgi_runner.rb +45 -0
  34. data/lib/webrick/httpservlet/cgihandler.rb +104 -0
  35. data/lib/webrick/httpservlet/erbhandler.rb +54 -0
  36. data/lib/webrick/httpservlet/filehandler.rb +398 -0
  37. data/lib/webrick/httpservlet/prochandler.rb +33 -0
  38. data/lib/webrick/httpstatus.rb +126 -0
  39. data/lib/webrick/httputils.rb +391 -0
  40. data/lib/webrick/httpversion.rb +49 -0
  41. data/lib/webrick/log.rb +88 -0
  42. data/lib/webrick/server.rb +200 -0
  43. data/lib/webrick/ssl.rb +126 -0
  44. data/lib/webrick/utils.rb +100 -0
  45. data/lib/webrick/version.rb +13 -0
  46. data/rubysl-webrick.gemspec +23 -0
  47. metadata +145 -0
@@ -0,0 +1,54 @@
1
+ #
2
+ # erbhandler.rb -- ERBHandler Class
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: erbhandler.rb,v 1.25 2003/02/24 19:25:31 gotoyuzo Exp $
10
+
11
+ require 'webrick/httpservlet/abstract.rb'
12
+
13
+ require 'erb'
14
+
15
+ module WEBrick
16
+ module HTTPServlet
17
+
18
+ class ERBHandler < AbstractServlet
19
+ def initialize(server, name)
20
+ super
21
+ @script_filename = name
22
+ end
23
+
24
+ def do_GET(req, res)
25
+ unless defined?(ERB)
26
+ @logger.warn "#{self.class}: ERB not defined."
27
+ raise HTTPStatus::Forbidden, "ERBHandler cannot work."
28
+ end
29
+ begin
30
+ data = open(@script_filename){|io| io.read }
31
+ res.body = evaluate(ERB.new(data), req, res)
32
+ res['content-type'] =
33
+ HTTPUtils::mime_type(@script_filename, @config[:MimeTypes])
34
+ rescue StandardError => ex
35
+ raise
36
+ rescue Exception => ex
37
+ @logger.error(ex)
38
+ raise HTTPStatus::InternalServerError, ex.message
39
+ end
40
+ end
41
+
42
+ alias do_POST do_GET
43
+
44
+ private
45
+ def evaluate(erb, servlet_request, servlet_response)
46
+ Module.new.module_eval{
47
+ meta_vars = servlet_request.meta_vars
48
+ query = servlet_request.query
49
+ erb.result(binding)
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,398 @@
1
+ #
2
+ # filehandler.rb -- FileHandler Module
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
+ # Copyright (c) 2003 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: filehandler.rb,v 1.44 2003/06/07 01:34:51 gotoyuzo Exp $
10
+
11
+ require 'thread'
12
+ require 'time'
13
+
14
+ require 'webrick/htmlutils'
15
+ require 'webrick/httputils'
16
+ require 'webrick/httpstatus'
17
+
18
+ module WEBrick
19
+ module HTTPServlet
20
+
21
+ class DefaultFileHandler < AbstractServlet
22
+ def initialize(server, local_path)
23
+ super
24
+ @local_path = local_path
25
+ end
26
+
27
+ def do_GET(req, res)
28
+ st = File::stat(@local_path)
29
+ mtime = st.mtime
30
+ res['etag'] = sprintf("%x-%x-%x", st.ino, st.size, st.mtime.to_i)
31
+
32
+ if not_modified?(req, res, mtime, res['etag'])
33
+ res.body = ''
34
+ raise HTTPStatus::NotModified
35
+ elsif req['range']
36
+ make_partial_content(req, res, @local_path, st.size)
37
+ raise HTTPStatus::PartialContent
38
+ else
39
+ mtype = HTTPUtils::mime_type(@local_path, @config[:MimeTypes])
40
+ res['content-type'] = mtype
41
+ res['content-length'] = st.size
42
+ res['last-modified'] = mtime.httpdate
43
+ res.body = open(@local_path, "rb")
44
+ end
45
+ end
46
+
47
+ def not_modified?(req, res, mtime, etag)
48
+ if ir = req['if-range']
49
+ begin
50
+ if Time.httpdate(ir) >= mtime
51
+ return true
52
+ end
53
+ rescue
54
+ if HTTPUtils::split_header_value(ir).member?(res['etag'])
55
+ return true
56
+ end
57
+ end
58
+ end
59
+
60
+ if (ims = req['if-modified-since']) && Time.parse(ims) >= mtime
61
+ return true
62
+ end
63
+
64
+ if (inm = req['if-none-match']) &&
65
+ HTTPUtils::split_header_value(inm).member?(res['etag'])
66
+ return true
67
+ end
68
+
69
+ return false
70
+ end
71
+
72
+ def make_partial_content(req, res, filename, filesize)
73
+ mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
74
+ unless ranges = HTTPUtils::parse_range_header(req['range'])
75
+ raise HTTPStatus::BadRequest,
76
+ "Unrecognized range-spec: \"#{req['range']}\""
77
+ end
78
+ open(filename, "rb"){|io|
79
+ if ranges.size > 1
80
+ time = Time.now
81
+ boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
82
+ body = ''
83
+ ranges.each{|range|
84
+ first, last = prepare_range(range, filesize)
85
+ next if first < 0
86
+ io.pos = first
87
+ content = io.read(last-first+1)
88
+ body << "--" << boundary << CRLF
89
+ body << "Content-Type: #{mtype}" << CRLF
90
+ body << "Content-Range: #{first}-#{last}/#{filesize}" << CRLF
91
+ body << CRLF
92
+ body << content
93
+ body << CRLF
94
+ }
95
+ raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
96
+ body << "--" << boundary << "--" << CRLF
97
+ res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
98
+ res.body = body
99
+ elsif range = ranges[0]
100
+ first, last = prepare_range(range, filesize)
101
+ raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
102
+ if last == filesize - 1
103
+ content = io.dup
104
+ content.pos = first
105
+ else
106
+ io.pos = first
107
+ content = io.read(last-first+1)
108
+ end
109
+ res['content-type'] = mtype
110
+ res['content-range'] = "#{first}-#{last}/#{filesize}"
111
+ res['content-length'] = last - first + 1
112
+ res.body = content
113
+ else
114
+ raise HTTPStatus::BadRequest
115
+ end
116
+ }
117
+ end
118
+
119
+ def prepare_range(range, filesize)
120
+ first = range.first < 0 ? filesize + range.first : range.first
121
+ return -1, -1 if first < 0 || first >= filesize
122
+ last = range.last < 0 ? filesize + range.last : range.last
123
+ last = filesize - 1 if last >= filesize
124
+ return first, last
125
+ end
126
+ end
127
+
128
+ class FileHandler < AbstractServlet
129
+ HandlerTable = Hash.new
130
+
131
+ def self.add_handler(suffix, handler)
132
+ HandlerTable[suffix] = handler
133
+ end
134
+
135
+ def self.remove_handler(suffix)
136
+ HandlerTable.delete(suffix)
137
+ end
138
+
139
+ def initialize(server, root, options={}, default=Config::FileHandler)
140
+ @config = server.config
141
+ @logger = @config[:Logger]
142
+ @root = File.expand_path(root)
143
+ if options == true || options == false
144
+ options = { :FancyIndexing => options }
145
+ end
146
+ @options = default.dup.update(options)
147
+ end
148
+
149
+ def service(req, res)
150
+ # if this class is mounted on "/" and /~username is requested.
151
+ # we're going to override path informations before invoking service.
152
+ if defined?(Etc) && @options[:UserDir] && req.script_name.empty?
153
+ if %r|^(/~([^/]+))| =~ req.path_info
154
+ script_name, user = $1, $2
155
+ path_info = $'
156
+ begin
157
+ passwd = Etc::getpwnam(user)
158
+ @root = File::join(passwd.dir, @options[:UserDir])
159
+ req.script_name = script_name
160
+ req.path_info = path_info
161
+ rescue
162
+ @logger.debug "#{self.class}#do_GET: getpwnam(#{user}) failed"
163
+ end
164
+ end
165
+ end
166
+ super(req, res)
167
+ end
168
+
169
+ def do_GET(req, res)
170
+ unless exec_handler(req, res)
171
+ set_dir_list(req, res)
172
+ end
173
+ end
174
+
175
+ def do_POST(req, res)
176
+ unless exec_handler(req, res)
177
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
178
+ end
179
+ end
180
+
181
+ def do_OPTIONS(req, res)
182
+ unless exec_handler(req, res)
183
+ super(req, res)
184
+ end
185
+ end
186
+
187
+ # ToDo
188
+ # RFC2518: HTTP Extensions for Distributed Authoring -- WEBDAV
189
+ #
190
+ # PROPFIND PROPPATCH MKCOL DELETE PUT COPY MOVE
191
+ # LOCK UNLOCK
192
+
193
+ # RFC3253: Versioning Extensions to WebDAV
194
+ # (Web Distributed Authoring and Versioning)
195
+ #
196
+ # VERSION-CONTROL REPORT CHECKOUT CHECK_IN UNCHECKOUT
197
+ # MKWORKSPACE UPDATE LABEL MERGE ACTIVITY
198
+
199
+ private
200
+
201
+ def exec_handler(req, res)
202
+ raise HTTPStatus::NotFound, "`#{req.path}' not found" unless @root
203
+ if set_filename(req, res)
204
+ handler = get_handler(req)
205
+ call_callback(:HandlerCallback, req, res)
206
+ h = handler.get_instance(@config, res.filename)
207
+ h.service(req, res)
208
+ return true
209
+ end
210
+ call_callback(:HandlerCallback, req, res)
211
+ return false
212
+ end
213
+
214
+ def get_handler(req)
215
+ suffix1 = (/\.(\w+)$/ =~ req.script_name) && $1.downcase
216
+ suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ req.script_name) && $1.downcase
217
+ handler_table = @options[:HandlerTable]
218
+ return handler_table[suffix1] || handler_table[suffix2] ||
219
+ HandlerTable[suffix1] || HandlerTable[suffix2] ||
220
+ DefaultFileHandler
221
+ end
222
+
223
+ def set_filename(req, res)
224
+ res.filename = @root.dup
225
+ path_info = req.path_info.scan(%r|/[^/]*|)
226
+
227
+ path_info.unshift("") # dummy for checking @root dir
228
+ while base = path_info.first
229
+ check_filename(req, res, base)
230
+ break if base == "/"
231
+ break unless File.directory?(res.filename + base)
232
+ shift_path_info(req, res, path_info)
233
+ call_callback(:DirectoryCallback, req, res)
234
+ end
235
+
236
+ if base = path_info.first
237
+ check_filename(req, res, base)
238
+ if base == "/"
239
+ if file = search_index_file(req, res)
240
+ shift_path_info(req, res, path_info, file)
241
+ call_callback(:FileCallback, req, res)
242
+ return true
243
+ end
244
+ shift_path_info(req, res, path_info)
245
+ elsif file = search_file(req, res, base)
246
+ shift_path_info(req, res, path_info, file)
247
+ call_callback(:FileCallback, req, res)
248
+ return true
249
+ else
250
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
251
+ end
252
+ end
253
+
254
+ return false
255
+ end
256
+
257
+ def check_filename(req, res, name)
258
+ @options[:NondisclosureName].each{|pattern|
259
+ if File.fnmatch("/#{pattern}", name)
260
+ @logger.warn("the request refers nondisclosure name `#{name}'.")
261
+ raise HTTPStatus::NotFound, "`#{req.path}' not found."
262
+ end
263
+ }
264
+ end
265
+
266
+ def shift_path_info(req, res, path_info, base=nil)
267
+ tmp = path_info.shift
268
+ base = base || tmp
269
+ req.path_info = path_info.join
270
+ req.script_name << base
271
+ res.filename << base
272
+ end
273
+
274
+ def search_index_file(req, res)
275
+ @config[:DirectoryIndex].each{|index|
276
+ if file = search_file(req, res, "/"+index)
277
+ return file
278
+ end
279
+ }
280
+ return nil
281
+ end
282
+
283
+ def search_file(req, res, basename)
284
+ langs = @options[:AcceptableLanguages]
285
+ path = res.filename + basename
286
+ if File.file?(path)
287
+ return basename
288
+ elsif langs.size > 0
289
+ req.accept_language.each{|lang|
290
+ path_with_lang = path + ".#{lang}"
291
+ if langs.member?(lang) && File.file?(path_with_lang)
292
+ return basename + ".#{lang}"
293
+ end
294
+ }
295
+ (langs - req.accept_language).each{|lang|
296
+ path_with_lang = path + ".#{lang}"
297
+ if File.file?(path_with_lang)
298
+ return basename + ".#{lang}"
299
+ end
300
+ }
301
+ end
302
+ return nil
303
+ end
304
+
305
+ def call_callback(callback_name, req, res)
306
+ if cb = @options[callback_name]
307
+ cb.call(req, res)
308
+ end
309
+ end
310
+
311
+ def nondisclosure_name?(name)
312
+ @options[:NondisclosureName].each{|pattern|
313
+ if File.fnmatch(pattern, name)
314
+ return true
315
+ end
316
+ }
317
+ return false
318
+ end
319
+
320
+ def set_dir_list(req, res)
321
+ redirect_to_directory_uri(req, res)
322
+ unless @options[:FancyIndexing]
323
+ raise HTTPStatus::Forbidden, "no access permission to `#{req.path}'"
324
+ end
325
+ local_path = res.filename
326
+ list = Dir::entries(local_path).collect{|name|
327
+ next if name == "." || name == ".."
328
+ next if nondisclosure_name?(name)
329
+ st = (File::stat(local_path + name) rescue nil)
330
+ if st.nil?
331
+ [ name, nil, -1 ]
332
+ elsif st.directory?
333
+ [ name + "/", st.mtime, -1 ]
334
+ else
335
+ [ name, st.mtime, st.size ]
336
+ end
337
+ }
338
+ list.compact!
339
+
340
+ if d0 = req.query["N"]; idx = 0
341
+ elsif d0 = req.query["M"]; idx = 1
342
+ elsif d0 = req.query["S"]; idx = 2
343
+ else d0 = "A" ; idx = 0
344
+ end
345
+ d1 = (d0 == "A") ? "D" : "A"
346
+
347
+ if d0 == "A"
348
+ list.sort!{|a,b| a[idx] <=> b[idx] }
349
+ else
350
+ list.sort!{|a,b| b[idx] <=> a[idx] }
351
+ end
352
+
353
+ res['content-type'] = "text/html"
354
+
355
+ res.body = <<-_end_of_html_
356
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
357
+ <HTML>
358
+ <HEAD><TITLE>Index of #{HTMLUtils::escape(req.path)}</TITLE></HEAD>
359
+ <BODY>
360
+ <H1>Index of #{HTMLUtils::escape(req.path)}</H1>
361
+ _end_of_html_
362
+
363
+ res.body << "<PRE>\n"
364
+ res.body << " <A HREF=\"?N=#{d1}\">Name</A> "
365
+ res.body << "<A HREF=\"?M=#{d1}\">Last modified</A> "
366
+ res.body << "<A HREF=\"?S=#{d1}\">Size</A>\n"
367
+ res.body << "<HR>\n"
368
+
369
+ list.unshift [ "..", File::mtime(local_path+".."), -1 ]
370
+ list.each{ |name, time, size|
371
+ if name == ".."
372
+ dname = "Parent Directory"
373
+ elsif name.size > 25
374
+ dname = name.sub(/^(.{23})(.*)/){ $1 + ".." }
375
+ else
376
+ dname = name
377
+ end
378
+ s = " <A HREF=\"#{HTTPUtils::escape(name)}\">#{dname}</A>"
379
+ s << " " * (30 - dname.size)
380
+ s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
381
+ s << (size >= 0 ? size.to_s : "-") << "\n"
382
+ res.body << s
383
+ }
384
+ res.body << "</PRE><HR>"
385
+
386
+ res.body << <<-_end_of_html_
387
+ <ADDRESS>
388
+ #{HTMLUtils::escape(@config[:ServerSoftware])}<BR>
389
+ at #{req.host}:#{req.port}
390
+ </ADDRESS>
391
+ </BODY>
392
+ </HTML>
393
+ _end_of_html_
394
+ end
395
+
396
+ end
397
+ end
398
+ end