webrick 1.3.1

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 (63) hide show
  1. data/README.txt +21 -0
  2. data/lib/webrick.rb +227 -0
  3. data/lib/webrick/accesslog.rb +151 -0
  4. data/lib/webrick/cgi.rb +260 -0
  5. data/lib/webrick/compat.rb +35 -0
  6. data/lib/webrick/config.rb +121 -0
  7. data/lib/webrick/cookie.rb +110 -0
  8. data/lib/webrick/htmlutils.rb +28 -0
  9. data/lib/webrick/httpauth.rb +95 -0
  10. data/lib/webrick/httpauth/authenticator.rb +112 -0
  11. data/lib/webrick/httpauth/basicauth.rb +108 -0
  12. data/lib/webrick/httpauth/digestauth.rb +392 -0
  13. data/lib/webrick/httpauth/htdigest.rb +128 -0
  14. data/lib/webrick/httpauth/htgroup.rb +93 -0
  15. data/lib/webrick/httpauth/htpasswd.rb +121 -0
  16. data/lib/webrick/httpauth/userdb.rb +52 -0
  17. data/lib/webrick/httpproxy.rb +305 -0
  18. data/lib/webrick/httprequest.rb +461 -0
  19. data/lib/webrick/httpresponse.rb +399 -0
  20. data/lib/webrick/https.rb +64 -0
  21. data/lib/webrick/httpserver.rb +264 -0
  22. data/lib/webrick/httpservlet.rb +22 -0
  23. data/lib/webrick/httpservlet/abstract.rb +153 -0
  24. data/lib/webrick/httpservlet/cgi_runner.rb +46 -0
  25. data/lib/webrick/httpservlet/cgihandler.rb +108 -0
  26. data/lib/webrick/httpservlet/erbhandler.rb +87 -0
  27. data/lib/webrick/httpservlet/filehandler.rb +470 -0
  28. data/lib/webrick/httpservlet/prochandler.rb +33 -0
  29. data/lib/webrick/httpstatus.rb +184 -0
  30. data/lib/webrick/httputils.rb +394 -0
  31. data/lib/webrick/httpversion.rb +49 -0
  32. data/lib/webrick/log.rb +136 -0
  33. data/lib/webrick/server.rb +218 -0
  34. data/lib/webrick/ssl.rb +127 -0
  35. data/lib/webrick/utils.rb +241 -0
  36. data/lib/webrick/version.rb +13 -0
  37. data/sample/webrick/demo-app.rb +66 -0
  38. data/sample/webrick/demo-multipart.cgi +12 -0
  39. data/sample/webrick/demo-servlet.rb +6 -0
  40. data/sample/webrick/demo-urlencoded.cgi +12 -0
  41. data/sample/webrick/hello.cgi +11 -0
  42. data/sample/webrick/hello.rb +8 -0
  43. data/sample/webrick/httpd.rb +23 -0
  44. data/sample/webrick/httpproxy.rb +25 -0
  45. data/sample/webrick/httpsd.rb +33 -0
  46. data/test/openssl/utils.rb +313 -0
  47. data/test/ruby/envutil.rb +208 -0
  48. data/test/webrick/test_cgi.rb +134 -0
  49. data/test/webrick/test_cookie.rb +131 -0
  50. data/test/webrick/test_filehandler.rb +285 -0
  51. data/test/webrick/test_httpauth.rb +167 -0
  52. data/test/webrick/test_httpproxy.rb +282 -0
  53. data/test/webrick/test_httprequest.rb +411 -0
  54. data/test/webrick/test_httpresponse.rb +49 -0
  55. data/test/webrick/test_httpserver.rb +305 -0
  56. data/test/webrick/test_httputils.rb +96 -0
  57. data/test/webrick/test_httpversion.rb +40 -0
  58. data/test/webrick/test_server.rb +67 -0
  59. data/test/webrick/test_utils.rb +64 -0
  60. data/test/webrick/utils.rb +58 -0
  61. data/test/webrick/webrick.cgi +36 -0
  62. data/test/webrick/webrick_long_filename.cgi +36 -0
  63. metadata +106 -0
@@ -0,0 +1,33 @@
1
+ #
2
+ # prochandler.rb -- ProcHandler 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: prochandler.rb,v 1.7 2002/09/21 12:23:42 gotoyuzo Exp $
10
+
11
+ require 'webrick/httpservlet/abstract.rb'
12
+
13
+ module WEBrick
14
+ module HTTPServlet
15
+
16
+ class ProcHandler < AbstractServlet
17
+ def get_instance(server, *options)
18
+ self
19
+ end
20
+
21
+ def initialize(proc)
22
+ @proc = proc
23
+ end
24
+
25
+ def do_GET(request, response)
26
+ @proc.call(request, response)
27
+ end
28
+
29
+ alias do_POST do_GET
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,184 @@
1
+ #--
2
+ # httpstatus.rb -- HTTPStatus Class
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: httpstatus.rb,v 1.11 2003/03/24 20:18:55 gotoyuzo Exp $
10
+
11
+ module WEBrick
12
+
13
+ ##
14
+ # This module is used to manager HTTP status codes.
15
+ #
16
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html for more
17
+ # information.
18
+ module HTTPStatus
19
+
20
+ ##
21
+ # Root of the HTTP status class hierarchy
22
+ class Status < StandardError
23
+ def initialize(*args) # :nodoc:
24
+ args[0] = AccessLog.escape(args[0]) unless args.empty?
25
+ super(*args)
26
+ end
27
+ class << self
28
+ attr_reader :code, :reason_phrase # :nodoc:
29
+ end
30
+
31
+ # Returns the HTTP status code
32
+ def code() self::class::code end
33
+
34
+ # Returns the HTTP status description
35
+ def reason_phrase() self::class::reason_phrase end
36
+
37
+ alias to_i code # :nodoc:
38
+ end
39
+
40
+ # Root of the HTTP info statuses
41
+ class Info < Status; end
42
+ # Root of the HTTP sucess statuses
43
+ class Success < Status; end
44
+ # Root of the HTTP redirect statuses
45
+ class Redirect < Status; end
46
+ # Root of the HTTP error statuses
47
+ class Error < Status; end
48
+ # Root of the HTTP client error statuses
49
+ class ClientError < Error; end
50
+ # Root of the HTTP server error statuses
51
+ class ServerError < Error; end
52
+
53
+ class EOFError < StandardError; end
54
+
55
+ # HTTP status codes and descriptions
56
+ StatusMessage = { # :nodoc:
57
+ 100 => 'Continue',
58
+ 101 => 'Switching Protocols',
59
+ 200 => 'OK',
60
+ 201 => 'Created',
61
+ 202 => 'Accepted',
62
+ 203 => 'Non-Authoritative Information',
63
+ 204 => 'No Content',
64
+ 205 => 'Reset Content',
65
+ 206 => 'Partial Content',
66
+ 300 => 'Multiple Choices',
67
+ 301 => 'Moved Permanently',
68
+ 302 => 'Found',
69
+ 303 => 'See Other',
70
+ 304 => 'Not Modified',
71
+ 305 => 'Use Proxy',
72
+ 307 => 'Temporary Redirect',
73
+ 400 => 'Bad Request',
74
+ 401 => 'Unauthorized',
75
+ 402 => 'Payment Required',
76
+ 403 => 'Forbidden',
77
+ 404 => 'Not Found',
78
+ 405 => 'Method Not Allowed',
79
+ 406 => 'Not Acceptable',
80
+ 407 => 'Proxy Authentication Required',
81
+ 408 => 'Request Timeout',
82
+ 409 => 'Conflict',
83
+ 410 => 'Gone',
84
+ 411 => 'Length Required',
85
+ 412 => 'Precondition Failed',
86
+ 413 => 'Request Entity Too Large',
87
+ 414 => 'Request-URI Too Large',
88
+ 415 => 'Unsupported Media Type',
89
+ 416 => 'Request Range Not Satisfiable',
90
+ 417 => 'Expectation Failed',
91
+ 500 => 'Internal Server Error',
92
+ 501 => 'Not Implemented',
93
+ 502 => 'Bad Gateway',
94
+ 503 => 'Service Unavailable',
95
+ 504 => 'Gateway Timeout',
96
+ 505 => 'HTTP Version Not Supported'
97
+ }
98
+
99
+ # Maps a status code to the corresponding Status class
100
+ CodeToError = {} # :nodoc:
101
+
102
+ # Creates a status or error class for each status code and
103
+ # populates the CodeToError map.
104
+ StatusMessage.each{|code, message|
105
+ message.freeze
106
+ var_name = message.gsub(/[ \-]/,'_').upcase
107
+ err_name = message.gsub(/[ \-]/,'')
108
+
109
+ case code
110
+ when 100...200; parent = Info
111
+ when 200...300; parent = Success
112
+ when 300...400; parent = Redirect
113
+ when 400...500; parent = ClientError
114
+ when 500...600; parent = ServerError
115
+ end
116
+
117
+ const_set("RC_#{var_name}", code)
118
+ err_class = Class.new(parent)
119
+ err_class.instance_variable_set(:@code, code)
120
+ err_class.instance_variable_set(:@reason_phrase, message)
121
+ const_set(err_name, err_class)
122
+ CodeToError[code] = err_class
123
+ }
124
+
125
+ ##
126
+ # Returns the description corresponding to the HTTP status +code+
127
+ #
128
+ # WEBrick::HTTPStatus.reason_phrase 404
129
+ # => "Not Found"
130
+ def reason_phrase(code)
131
+ StatusMessage[code.to_i]
132
+ end
133
+
134
+ ##
135
+ # Is +code+ an informational status?
136
+ def info?(code)
137
+ code.to_i >= 100 and code.to_i < 200
138
+ end
139
+
140
+ ##
141
+ # Is +code+ a successful status?
142
+ def success?(code)
143
+ code.to_i >= 200 and code.to_i < 300
144
+ end
145
+
146
+ ##
147
+ # Is +code+ a redirection status?
148
+ def redirect?(code)
149
+ code.to_i >= 300 and code.to_i < 400
150
+ end
151
+
152
+ ##
153
+ # Is +code+ an error status?
154
+ def error?(code)
155
+ code.to_i >= 400 and code.to_i < 600
156
+ end
157
+
158
+ ##
159
+ # Is +code+ a client error status?
160
+ def client_error?(code)
161
+ code.to_i >= 400 and code.to_i < 500
162
+ end
163
+
164
+ ##
165
+ # Is +code+ a server error status?
166
+ def server_error?(code)
167
+ code.to_i >= 500 and code.to_i < 600
168
+ end
169
+
170
+ ##
171
+ # Returns the status class corresponding to +code+
172
+ #
173
+ # WEBrick::HTTPStatus[302]
174
+ # => WEBrick::HTTPStatus::NotFound
175
+ #
176
+ def self.[](code)
177
+ CodeToError[code]
178
+ end
179
+
180
+ module_function :reason_phrase
181
+ module_function :info?, :success?, :redirect?, :error?
182
+ module_function :client_error?, :server_error?
183
+ end
184
+ end
@@ -0,0 +1,394 @@
1
+ #
2
+ # httputils.rb -- HTTPUtils Module
3
+ #
4
+ # Author: IPR -- Internet Programming with Ruby -- writers
5
+ # Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6
+ # Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7
+ # reserved.
8
+ #
9
+ # $IPR: httputils.rb,v 1.34 2003/06/05 21:34:08 gotoyuzo Exp $
10
+
11
+ require 'socket'
12
+ require 'tempfile'
13
+
14
+ module WEBrick
15
+ CR = "\x0d"
16
+ LF = "\x0a"
17
+ CRLF = "\x0d\x0a"
18
+
19
+ module HTTPUtils
20
+
21
+ def normalize_path(path)
22
+ raise "abnormal path `#{path}'" if path[0] != ?/
23
+ ret = path.dup
24
+
25
+ ret.gsub!(%r{/+}o, '/') # // => /
26
+ while ret.sub!(%r'/\.(?:/|\Z)', '/'); end # /. => /
27
+ while ret.sub!(%r'/(?!\.\./)[^/]+/\.\.(?:/|\Z)', '/'); end # /foo/.. => /foo
28
+
29
+ raise "abnormal path `#{path}'" if %r{/\.\.(/|\Z)} =~ ret
30
+ ret
31
+ end
32
+ module_function :normalize_path
33
+
34
+ #####
35
+
36
+ DefaultMimeTypes = {
37
+ "ai" => "application/postscript",
38
+ "asc" => "text/plain",
39
+ "avi" => "video/x-msvideo",
40
+ "bin" => "application/octet-stream",
41
+ "bmp" => "image/bmp",
42
+ "class" => "application/octet-stream",
43
+ "cer" => "application/pkix-cert",
44
+ "crl" => "application/pkix-crl",
45
+ "crt" => "application/x-x509-ca-cert",
46
+ #"crl" => "application/x-pkcs7-crl",
47
+ "css" => "text/css",
48
+ "dms" => "application/octet-stream",
49
+ "doc" => "application/msword",
50
+ "dvi" => "application/x-dvi",
51
+ "eps" => "application/postscript",
52
+ "etx" => "text/x-setext",
53
+ "exe" => "application/octet-stream",
54
+ "gif" => "image/gif",
55
+ "htm" => "text/html",
56
+ "html" => "text/html",
57
+ "jpe" => "image/jpeg",
58
+ "jpeg" => "image/jpeg",
59
+ "jpg" => "image/jpeg",
60
+ "js" => "application/javascript",
61
+ "lha" => "application/octet-stream",
62
+ "lzh" => "application/octet-stream",
63
+ "mov" => "video/quicktime",
64
+ "mpe" => "video/mpeg",
65
+ "mpeg" => "video/mpeg",
66
+ "mpg" => "video/mpeg",
67
+ "pbm" => "image/x-portable-bitmap",
68
+ "pdf" => "application/pdf",
69
+ "pgm" => "image/x-portable-graymap",
70
+ "png" => "image/png",
71
+ "pnm" => "image/x-portable-anymap",
72
+ "ppm" => "image/x-portable-pixmap",
73
+ "ppt" => "application/vnd.ms-powerpoint",
74
+ "ps" => "application/postscript",
75
+ "qt" => "video/quicktime",
76
+ "ras" => "image/x-cmu-raster",
77
+ "rb" => "text/plain",
78
+ "rd" => "text/plain",
79
+ "rtf" => "application/rtf",
80
+ "sgm" => "text/sgml",
81
+ "sgml" => "text/sgml",
82
+ "svg" => "image/svg+xml",
83
+ "tif" => "image/tiff",
84
+ "tiff" => "image/tiff",
85
+ "txt" => "text/plain",
86
+ "xbm" => "image/x-xbitmap",
87
+ "xhtml" => "text/html",
88
+ "xls" => "application/vnd.ms-excel",
89
+ "xml" => "text/xml",
90
+ "xpm" => "image/x-xpixmap",
91
+ "xwd" => "image/x-xwindowdump",
92
+ "zip" => "application/zip",
93
+ }
94
+
95
+ # Load Apache compatible mime.types file.
96
+ def load_mime_types(file)
97
+ open(file){ |io|
98
+ hash = Hash.new
99
+ io.each{ |line|
100
+ next if /^#/ =~ line
101
+ line.chomp!
102
+ mimetype, ext0 = line.split(/\s+/, 2)
103
+ next unless ext0
104
+ next if ext0.empty?
105
+ ext0.split(/\s+/).each{ |ext| hash[ext] = mimetype }
106
+ }
107
+ hash
108
+ }
109
+ end
110
+ module_function :load_mime_types
111
+
112
+ def mime_type(filename, mime_tab)
113
+ suffix1 = (/\.(\w+)$/ =~ filename && $1.downcase)
114
+ suffix2 = (/\.(\w+)\.[\w\-]+$/ =~ filename && $1.downcase)
115
+ mime_tab[suffix1] || mime_tab[suffix2] || "application/octet-stream"
116
+ end
117
+ module_function :mime_type
118
+
119
+ #####
120
+
121
+ def parse_header(raw)
122
+ header = Hash.new([].freeze)
123
+ field = nil
124
+ raw.each_line{|line|
125
+ case line
126
+ when /^([A-Za-z0-9!\#$%&'*+\-.^_`|~]+):\s*(.*?)\s*\z/om
127
+ field, value = $1, $2
128
+ field.downcase!
129
+ header[field] = [] unless header.has_key?(field)
130
+ header[field] << value
131
+ when /^\s+(.*?)\s*\z/om
132
+ value = $1
133
+ unless field
134
+ raise HTTPStatus::BadRequest, "bad header '#{line}'."
135
+ end
136
+ header[field][-1] << " " << value
137
+ else
138
+ raise HTTPStatus::BadRequest, "bad header '#{line}'."
139
+ end
140
+ }
141
+ header.each{|key, values|
142
+ values.each{|value|
143
+ value.strip!
144
+ value.gsub!(/\s+/, " ")
145
+ }
146
+ }
147
+ header
148
+ end
149
+ module_function :parse_header
150
+
151
+ def split_header_value(str)
152
+ str.scan(%r'\G((?:"(?:\\.|[^"])+?"|[^",]+)+)
153
+ (?:,\s*|\Z)'xn).flatten
154
+ end
155
+ module_function :split_header_value
156
+
157
+ def parse_range_header(ranges_specifier)
158
+ if /^bytes=(.*)/ =~ ranges_specifier
159
+ byte_range_set = split_header_value($1)
160
+ byte_range_set.collect{|range_spec|
161
+ case range_spec
162
+ when /^(\d+)-(\d+)/ then $1.to_i .. $2.to_i
163
+ when /^(\d+)-/ then $1.to_i .. -1
164
+ when /^-(\d+)/ then -($1.to_i) .. -1
165
+ else return nil
166
+ end
167
+ }
168
+ end
169
+ end
170
+ module_function :parse_range_header
171
+
172
+ def parse_qvalues(value)
173
+ tmp = []
174
+ if value
175
+ parts = value.split(/,\s*/)
176
+ parts.each {|part|
177
+ if m = %r{^([^\s,]+?)(?:;\s*q=(\d+(?:\.\d+)?))?$}.match(part)
178
+ val = m[1]
179
+ q = (m[2] or 1).to_f
180
+ tmp.push([val, q])
181
+ end
182
+ }
183
+ tmp = tmp.sort_by{|val, q| -q}
184
+ tmp.collect!{|val, q| val}
185
+ end
186
+ return tmp
187
+ end
188
+ module_function :parse_qvalues
189
+
190
+ #####
191
+
192
+ def dequote(str)
193
+ ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
194
+ ret.gsub!(/\\(.)/, "\\1")
195
+ ret
196
+ end
197
+ module_function :dequote
198
+
199
+ def quote(str)
200
+ '"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
201
+ end
202
+ module_function :quote
203
+
204
+ #####
205
+
206
+ class FormData < String
207
+ EmptyRawHeader = [].freeze
208
+ EmptyHeader = {}.freeze
209
+
210
+ attr_accessor :name, :filename, :next_data
211
+ protected :next_data
212
+
213
+ def initialize(*args)
214
+ @name = @filename = @next_data = nil
215
+ if args.empty?
216
+ @raw_header = []
217
+ @header = nil
218
+ super("")
219
+ else
220
+ @raw_header = EmptyRawHeader
221
+ @header = EmptyHeader
222
+ super(args.shift)
223
+ unless args.empty?
224
+ @next_data = self.class.new(*args)
225
+ end
226
+ end
227
+ end
228
+
229
+ def [](*key)
230
+ begin
231
+ @header[key[0].downcase].join(", ")
232
+ rescue StandardError, NameError
233
+ super
234
+ end
235
+ end
236
+
237
+ def <<(str)
238
+ if @header
239
+ super
240
+ elsif str == CRLF
241
+ @header = HTTPUtils::parse_header(@raw_header.join)
242
+ if cd = self['content-disposition']
243
+ if /\s+name="(.*?)"/ =~ cd then @name = $1 end
244
+ if /\s+filename="(.*?)"/ =~ cd then @filename = $1 end
245
+ end
246
+ else
247
+ @raw_header << str
248
+ end
249
+ self
250
+ end
251
+
252
+ def append_data(data)
253
+ tmp = self
254
+ while tmp
255
+ unless tmp.next_data
256
+ tmp.next_data = data
257
+ break
258
+ end
259
+ tmp = tmp.next_data
260
+ end
261
+ self
262
+ end
263
+
264
+ def each_data
265
+ tmp = self
266
+ while tmp
267
+ next_data = tmp.next_data
268
+ yield(tmp)
269
+ tmp = next_data
270
+ end
271
+ end
272
+
273
+ def list
274
+ ret = []
275
+ each_data{|data|
276
+ ret << data.to_s
277
+ }
278
+ ret
279
+ end
280
+
281
+ alias :to_ary :list
282
+
283
+ def to_s
284
+ String.new(self)
285
+ end
286
+ end
287
+
288
+ def parse_query(str)
289
+ query = Hash.new
290
+ if str
291
+ str.split(/[&;]/).each{|x|
292
+ next if x.empty?
293
+ key, val = x.split(/=/,2)
294
+ key = unescape_form(key)
295
+ val = unescape_form(val.to_s)
296
+ val = FormData.new(val)
297
+ val.name = key
298
+ if query.has_key?(key)
299
+ query[key].append_data(val)
300
+ next
301
+ end
302
+ query[key] = val
303
+ }
304
+ end
305
+ query
306
+ end
307
+ module_function :parse_query
308
+
309
+ def parse_form_data(io, boundary)
310
+ boundary_regexp = /\A--#{Regexp.quote(boundary)}(--)?#{CRLF}\z/
311
+ form_data = Hash.new
312
+ return form_data unless io
313
+ data = nil
314
+ io.each_line{|line|
315
+ if boundary_regexp =~ line
316
+ if data
317
+ data.chop!
318
+ key = data.name
319
+ if form_data.has_key?(key)
320
+ form_data[key].append_data(data)
321
+ else
322
+ form_data[key] = data
323
+ end
324
+ end
325
+ data = FormData.new
326
+ next
327
+ else
328
+ if data
329
+ data << line
330
+ end
331
+ end
332
+ }
333
+ return form_data
334
+ end
335
+ module_function :parse_form_data
336
+
337
+ #####
338
+
339
+ reserved = ';/?:@&=+$,'
340
+ num = '0123456789'
341
+ lowalpha = 'abcdefghijklmnopqrstuvwxyz'
342
+ upalpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
343
+ mark = '-_.!~*\'()'
344
+ unreserved = num + lowalpha + upalpha + mark
345
+ control = (0x0..0x1f).collect{|c| c.chr }.join + "\x7f"
346
+ space = " "
347
+ delims = '<>#%"'
348
+ unwise = '{}|\\^[]`'
349
+ nonascii = (0x80..0xff).collect{|c| c.chr }.join
350
+
351
+ module_function
352
+
353
+ def _make_regex(str) /([#{Regexp.escape(str)}])/n end
354
+ def _make_regex!(str) /([^#{Regexp.escape(str)}])/n end
355
+ def _escape(str, regex) str.gsub(regex){ "%%%02X" % $1.ord } end
356
+ def _unescape(str, regex) str.gsub(regex){ $1.hex.chr } end
357
+
358
+ UNESCAPED = _make_regex(control+space+delims+unwise+nonascii)
359
+ UNESCAPED_FORM = _make_regex(reserved+control+delims+unwise+nonascii)
360
+ NONASCII = _make_regex(nonascii)
361
+ ESCAPED = /%([0-9a-fA-F]{2})/
362
+ UNESCAPED_PCHAR = _make_regex!(unreserved+":@&=+$,")
363
+
364
+ def escape(str)
365
+ _escape(str, UNESCAPED)
366
+ end
367
+
368
+ def unescape(str)
369
+ _unescape(str, ESCAPED)
370
+ end
371
+
372
+ def escape_form(str)
373
+ ret = _escape(str, UNESCAPED_FORM)
374
+ ret.gsub!(/ /, "+")
375
+ ret
376
+ end
377
+
378
+ def unescape_form(str)
379
+ _unescape(str.gsub(/\+/, " "), ESCAPED)
380
+ end
381
+
382
+ def escape_path(str)
383
+ result = ""
384
+ str.scan(%r{/([^/]*)}).each{|i|
385
+ result << "/" << _escape(i[0], UNESCAPED_PCHAR)
386
+ }
387
+ return result
388
+ end
389
+
390
+ def escape8bit(str)
391
+ _escape(str, NONASCII)
392
+ end
393
+ end
394
+ end