rack 0.4.0 → 0.9.0

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

Potentially problematic release.


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

Files changed (59) hide show
  1. data/RDOX +61 -3
  2. data/README +52 -37
  3. data/Rakefile +9 -0
  4. data/SPEC +6 -3
  5. data/bin/rackup +0 -0
  6. data/lib/rack.rb +7 -2
  7. data/lib/rack/adapter/camping.rb +1 -1
  8. data/lib/rack/auth/openid.rb +4 -3
  9. data/lib/rack/builder.rb +12 -1
  10. data/lib/rack/conditionalget.rb +43 -0
  11. data/lib/rack/content_length.rb +25 -0
  12. data/lib/rack/deflater.rb +29 -5
  13. data/lib/rack/directory.rb +82 -91
  14. data/lib/rack/file.rb +45 -76
  15. data/lib/rack/handler.rb +4 -0
  16. data/lib/rack/handler/evented_mongrel.rb +1 -1
  17. data/lib/rack/handler/fastcgi.rb +2 -0
  18. data/lib/rack/handler/mongrel.rb +6 -2
  19. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  20. data/lib/rack/handler/thin.rb +15 -0
  21. data/lib/rack/handler/webrick.rb +8 -4
  22. data/lib/rack/head.rb +19 -0
  23. data/lib/rack/lint.rb +74 -10
  24. data/lib/rack/lobster.rb +13 -13
  25. data/lib/rack/methodoverride.rb +27 -0
  26. data/lib/rack/mime.rb +204 -0
  27. data/lib/rack/request.rb +10 -1
  28. data/lib/rack/response.rb +7 -2
  29. data/lib/rack/session/abstract/id.rb +14 -1
  30. data/lib/rack/session/cookie.rb +19 -1
  31. data/lib/rack/session/memcache.rb +1 -1
  32. data/lib/rack/session/pool.rb +1 -1
  33. data/lib/rack/showexceptions.rb +5 -1
  34. data/lib/rack/showstatus.rb +3 -2
  35. data/lib/rack/urlmap.rb +1 -1
  36. data/lib/rack/utils.rb +42 -13
  37. data/test/cgi/lighttpd.conf +1 -1
  38. data/test/cgi/test +0 -0
  39. data/test/cgi/test.fcgi +0 -0
  40. data/test/cgi/test.ru +0 -0
  41. data/test/spec_rack_builder.rb +34 -0
  42. data/test/spec_rack_conditionalget.rb +41 -0
  43. data/test/spec_rack_content_length.rb +43 -0
  44. data/test/spec_rack_deflater.rb +49 -14
  45. data/test/spec_rack_file.rb +7 -0
  46. data/test/spec_rack_handler.rb +3 -3
  47. data/test/spec_rack_head.rb +30 -0
  48. data/test/spec_rack_lint.rb +79 -2
  49. data/test/spec_rack_methodoverride.rb +60 -0
  50. data/test/spec_rack_mock.rb +1 -1
  51. data/test/spec_rack_mongrel.rb +20 -1
  52. data/test/spec_rack_request.rb +46 -1
  53. data/test/spec_rack_response.rb +10 -3
  54. data/test/spec_rack_session_cookie.rb +33 -0
  55. data/test/spec_rack_thin.rb +90 -0
  56. data/test/spec_rack_utils.rb +20 -18
  57. data/test/spec_rack_webrick.rb +17 -0
  58. data/test/testrequest.rb +12 -0
  59. metadata +91 -5
data/lib/rack/handler.rb CHANGED
@@ -29,16 +29,20 @@ module Rack
29
29
  autoload :FastCGI, "rack/handler/fastcgi"
30
30
  autoload :Mongrel, "rack/handler/mongrel"
31
31
  autoload :EventedMongrel, "rack/handler/evented_mongrel"
32
+ autoload :SwiftipliedMongrel, "rack/handler/swiftiplied_mongrel"
32
33
  autoload :WEBrick, "rack/handler/webrick"
33
34
  autoload :LSWS, "rack/handler/lsws"
34
35
  autoload :SCGI, "rack/handler/scgi"
36
+ autoload :Thin, "rack/handler/thin"
35
37
 
36
38
  register 'cgi', 'Rack::Handler::CGI'
37
39
  register 'fastcgi', 'Rack::Handler::FastCGI'
38
40
  register 'mongrel', 'Rack::Handler::Mongrel'
39
41
  register 'emongrel', 'Rack::Handler::EventedMongrel'
42
+ register 'smongrel', 'Rack::Handler::SwiftipliedMongrel'
40
43
  register 'webrick', 'Rack::Handler::WEBrick'
41
44
  register 'lsws', 'Rack::Handler::LSWS'
42
45
  register 'scgi', 'Rack::Handler::SCGI'
46
+ register 'thin', 'Rack::Handler::Thin'
43
47
  end
44
48
  end
@@ -2,7 +2,7 @@ require 'swiftcore/evented_mongrel'
2
2
 
3
3
  module Rack
4
4
  module Handler
5
- class EventedMongrel < Mongrel
5
+ class EventedMongrel < Handler::Mongrel
6
6
  end
7
7
  end
8
8
  end
@@ -51,6 +51,8 @@ module Rack
51
51
  env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
52
52
  env["REQUEST_PATH"] ||= "/"
53
53
  env.delete "PATH_INFO" if env["PATH_INFO"] == ""
54
+ env.delete "CONTENT_TYPE" if env["CONTENT_TYPE"] == ""
55
+ env.delete "CONTENT_LENGTH" if env["CONTENT_LENGTH"] == ""
54
56
 
55
57
  status, headers, body = app.call(env)
56
58
  begin
@@ -60,15 +60,19 @@ module Rack
60
60
 
61
61
  begin
62
62
  response.status = status.to_i
63
+ response.send_status(nil)
64
+
63
65
  headers.each { |k, vs|
64
66
  vs.each { |v|
65
67
  response.header[k] = v
66
68
  }
67
69
  }
70
+ response.send_header
71
+
68
72
  body.each { |part|
69
- response.body << part
73
+ response.write part
74
+ response.socket.flush
70
75
  }
71
- response.finished
72
76
  ensure
73
77
  body.close if body.respond_to? :close
74
78
  end
@@ -0,0 +1,8 @@
1
+ require 'swiftcore/swiftiplied_mongrel'
2
+
3
+ module Rack
4
+ module Handler
5
+ class SwiftipliedMongrel < Handler::Mongrel
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ require "thin"
2
+
3
+ module Rack
4
+ module Handler
5
+ class Thin
6
+ def self.run(app, options={})
7
+ server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
8
+ options[:Port] || 8080,
9
+ app)
10
+ yield server if block_given?
11
+ server.start
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,7 @@ require 'stringio'
3
3
 
4
4
  module Rack
5
5
  module Handler
6
- class WEBrick < WEBrick::HTTPServlet::AbstractServlet
6
+ class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
7
7
  def self.run(app, options={})
8
8
  server = ::WEBrick::HTTPServer.new(options)
9
9
  server.mount "/", Rack::Handler::WEBrick, app
@@ -41,9 +41,13 @@ module Rack
41
41
  begin
42
42
  res.status = status.to_i
43
43
  headers.each { |k, vs|
44
- vs.each { |v|
45
- res[k] = v
46
- }
44
+ if k.downcase == "set-cookie"
45
+ res.cookies.concat vs.to_a
46
+ else
47
+ vs.each { |v|
48
+ res[k] = v
49
+ }
50
+ end
47
51
  }
48
52
  body.each { |part|
49
53
  res.body << part
data/lib/rack/head.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Rack
2
+
3
+ class Head
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ status, headers, body = @app.call(env)
10
+
11
+ if env["REQUEST_METHOD"] == "HEAD"
12
+ [status, headers, []]
13
+ else
14
+ [status, headers, body]
15
+ end
16
+ end
17
+ end
18
+
19
+ end
data/lib/rack/lint.rb CHANGED
@@ -29,8 +29,8 @@ module Rack
29
29
 
30
30
  ## A Rack application is an Ruby object (not a class) that
31
31
  ## responds to +call+.
32
- def call(env=nil)
33
- dup._call(env)
32
+ def call(env=nil)
33
+ dup._call(env)
34
34
  end
35
35
 
36
36
  def _call(env)
@@ -49,6 +49,7 @@ module Rack
49
49
  check_headers headers
50
50
  ## and the *body*.
51
51
  check_content_type status, headers
52
+ check_content_length status, headers, env
52
53
  [status, headers, self]
53
54
  end
54
55
 
@@ -162,11 +163,9 @@ module Rack
162
163
  ## * There must be a valid error stream in <tt>rack.errors</tt>.
163
164
  check_error env["rack.errors"]
164
165
 
165
- ## * The <tt>REQUEST_METHOD</tt> must be one of +GET+, +POST+, +PUT+,
166
- ## +DELETE+, +HEAD+, +OPTIONS+, +TRACE+.
166
+ ## * The <tt>REQUEST_METHOD</tt> must be a valid token.
167
167
  assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
168
- %w[GET POST PUT DELETE
169
- HEAD OPTIONS TRACE].include?(env["REQUEST_METHOD"])
168
+ env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
170
169
  }
171
170
 
172
171
  ## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
@@ -215,6 +214,14 @@ module Rack
215
214
  @input = input
216
215
  end
217
216
 
217
+ def size
218
+ @input.size
219
+ end
220
+
221
+ def rewind
222
+ @input.rewind
223
+ end
224
+
218
225
  ## * +gets+ must be called without arguments and return a string,
219
226
  ## or +nil+ on EOF.
220
227
  def gets(*args)
@@ -350,18 +357,75 @@ module Rack
350
357
  def check_content_type(status, headers)
351
358
  headers.each { |key, value|
352
359
  ## There must be a <tt>Content-Type</tt>, except when the
353
- ## +Status+ is 204 or 304, in which case there must be none
360
+ ## +Status+ is 1xx, 204 or 304, in which case there must be none
354
361
  ## given.
355
362
  if key.downcase == "content-type"
356
- assert("Content-Type header found in #{status} response, not allowed"){
357
- not [204, 304].include? status.to_i
363
+ assert("Content-Type header found in #{status} response, not allowed") {
364
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
358
365
  }
359
366
  return
360
367
  end
361
368
  }
362
369
  assert("No Content-Type header found") {
363
- [204, 304].include? status.to_i
370
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
371
+ }
372
+ end
373
+
374
+ ## === The Content-Length
375
+ def check_content_length(status, headers, env)
376
+ chunked_response = false
377
+ headers.each { |key, value|
378
+ if key.downcase == 'transfer-encoding'
379
+ chunked_response = value.downcase != 'identity'
380
+ end
381
+ }
382
+
383
+ headers.each { |key, value|
384
+ if key.downcase == 'content-length'
385
+ ## There must be a <tt>Content-Length</tt>, except when the
386
+ ## +Status+ is 1xx, 204 or 304, in which case there must be none
387
+ ## given.
388
+ assert("Content-Length header found in #{status} response, not allowed") {
389
+ not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
390
+ }
391
+
392
+ assert('Content-Length header should not be used if body is chunked') {
393
+ not chunked_response
394
+ }
395
+
396
+ bytes = 0
397
+ string_body = true
398
+
399
+ @body.each { |part|
400
+ unless part.kind_of?(String)
401
+ string_body = false
402
+ break
403
+ end
404
+
405
+ bytes += (part.respond_to?(:bytesize) ? part.bytesize : part.size)
406
+ }
407
+
408
+ if env["REQUEST_METHOD"] == "HEAD"
409
+ assert("Response body was given for HEAD request, but should be empty") {
410
+ bytes == 0
411
+ }
412
+ else
413
+ if string_body
414
+ assert("Content-Length header was #{value}, but should be #{bytes}") {
415
+ value == bytes.to_s
416
+ }
417
+ end
418
+ end
419
+
420
+ return
421
+ end
364
422
  }
423
+
424
+ if [ String, Array ].include?(@body.class) && !chunked_response
425
+ assert('No Content-Length header found') {
426
+ Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
427
+ }
428
+ end
365
429
  end
366
430
 
367
431
  ## === The Body
data/lib/rack/lobster.rb CHANGED
@@ -22,11 +22,11 @@ module Rack
22
22
  href = "?flip"
23
23
  end
24
24
 
25
- [200, {"Content-Type" => "text/html"},
26
- ["<title>Lobstericious!</title>",
27
- "<pre>", lobster, "</pre>",
28
- "<a href='#{href}'>flip!</a>"]
29
- ]
25
+ content = ["<title>Lobstericious!</title>",
26
+ "<pre>", lobster, "</pre>",
27
+ "<a href='#{href}'>flip!</a>"]
28
+ length = content.inject(0) { |a,e| a+e.size }.to_s
29
+ [200, {"Content-Type" => "text/html", "Content-Length" => length}, content]
30
30
  }
31
31
 
32
32
  def call(env)
@@ -43,14 +43,14 @@ module Rack
43
43
  href = "?flip=left"
44
44
  end
45
45
 
46
- Response.new.finish do |res|
47
- res.write "<title>Lobstericious!</title>"
48
- res.write "<pre>"
49
- res.write lobster
50
- res.write "</pre>"
51
- res.write "<p><a href='#{href}'>flip!</a></p>"
52
- res.write "<p><a href='?flip=crash'>crash!</a></p>"
53
- end
46
+ res = Response.new
47
+ res.write "<title>Lobstericious!</title>"
48
+ res.write "<pre>"
49
+ res.write lobster
50
+ res.write "</pre>"
51
+ res.write "<p><a href='#{href}'>flip!</a></p>"
52
+ res.write "<p><a href='?flip=crash'>crash!</a></p>"
53
+ res.finish
54
54
  end
55
55
 
56
56
  end
@@ -0,0 +1,27 @@
1
+ module Rack
2
+ class MethodOverride
3
+ HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
4
+
5
+ METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
6
+ HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ if env["REQUEST_METHOD"] == "POST"
14
+ req = Request.new(env)
15
+ method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
16
+ env[HTTP_METHOD_OVERRIDE_HEADER]
17
+ method = method.to_s.upcase
18
+ if HTTP_METHODS.include?(method)
19
+ env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
20
+ env["REQUEST_METHOD"] = method
21
+ end
22
+ end
23
+
24
+ @app.call(env)
25
+ end
26
+ end
27
+ end
data/lib/rack/mime.rb ADDED
@@ -0,0 +1,204 @@
1
+ module Rack
2
+ module Mime
3
+ # Returns String with mime type if found, otherwise use +fallback+.
4
+ # +ext+ should be filename extension in the '.ext' format that
5
+ # File.extname(file) returns.
6
+ # +fallback+ may be any object
7
+ #
8
+ # Also see the documentation for MIME_TYPES
9
+ #
10
+ # Usage:
11
+ # Rack::Utils.mime_type('.foo')
12
+ #
13
+ # This is a shortcut for:
14
+ # Rack::Utils::MIME_TYPES.fetch('.foo', 'application/octet-stream')
15
+
16
+ def mime_type(ext, fallback='application/octet-stream')
17
+ MIME_TYPES.fetch(ext, fallback)
18
+ end
19
+ module_function :mime_type
20
+
21
+ # List of most common mime-types, selected various sources
22
+ # according to their usefulness in a webserving scope for Ruby
23
+ # users.
24
+ #
25
+ # To amend this list with your local mime.types list you can use:
26
+ #
27
+ # require 'webrick/httputils'
28
+ # list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
29
+ # Rack::Utils::MIME_TYPES.merge!(list)
30
+ #
31
+ # To add the list mongrel provides, use:
32
+ #
33
+ # require 'mongrel/handlers'
34
+ # Rack::Utils::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
35
+
36
+ MIME_TYPES = {
37
+ ".3gp" => "video/3gpp",
38
+ ".a" => "application/octet-stream",
39
+ ".ai" => "application/postscript",
40
+ ".aif" => "audio/x-aiff",
41
+ ".aiff" => "audio/x-aiff",
42
+ ".asc" => "application/pgp-signature",
43
+ ".asf" => "video/x-ms-asf",
44
+ ".asm" => "text/x-asm",
45
+ ".asx" => "video/x-ms-asf",
46
+ ".atom" => "application/atom+xml",
47
+ ".au" => "audio/basic",
48
+ ".avi" => "video/x-msvideo",
49
+ ".bat" => "application/x-msdownload",
50
+ ".bin" => "application/octet-stream",
51
+ ".bmp" => "image/bmp",
52
+ ".bz2" => "application/x-bzip2",
53
+ ".c" => "text/x-c",
54
+ ".cab" => "application/vnd.ms-cab-compressed",
55
+ ".cc" => "text/x-c",
56
+ ".chm" => "application/vnd.ms-htmlhelp",
57
+ ".class" => "application/octet-stream",
58
+ ".com" => "application/x-msdownload",
59
+ ".conf" => "text/plain",
60
+ ".cpp" => "text/x-c",
61
+ ".crt" => "application/x-x509-ca-cert",
62
+ ".css" => "text/css",
63
+ ".csv" => "text/csv",
64
+ ".cxx" => "text/x-c",
65
+ ".deb" => "application/x-debian-package",
66
+ ".der" => "application/x-x509-ca-cert",
67
+ ".diff" => "text/x-diff",
68
+ ".djv" => "image/vnd.djvu",
69
+ ".djvu" => "image/vnd.djvu",
70
+ ".dll" => "application/x-msdownload",
71
+ ".dmg" => "application/octet-stream",
72
+ ".doc" => "application/msword",
73
+ ".dot" => "application/msword",
74
+ ".dtd" => "application/xml-dtd",
75
+ ".dvi" => "application/x-dvi",
76
+ ".ear" => "application/java-archive",
77
+ ".eml" => "message/rfc822",
78
+ ".eps" => "application/postscript",
79
+ ".exe" => "application/x-msdownload",
80
+ ".f" => "text/x-fortran",
81
+ ".f77" => "text/x-fortran",
82
+ ".f90" => "text/x-fortran",
83
+ ".flv" => "video/x-flv",
84
+ ".for" => "text/x-fortran",
85
+ ".gem" => "application/octet-stream",
86
+ ".gemspec" => "text/x-script.ruby",
87
+ ".gif" => "image/gif",
88
+ ".gz" => "application/x-gzip",
89
+ ".h" => "text/x-c",
90
+ ".hh" => "text/x-c",
91
+ ".htm" => "text/html",
92
+ ".html" => "text/html",
93
+ ".ico" => "image/vnd.microsoft.icon",
94
+ ".ics" => "text/calendar",
95
+ ".ifb" => "text/calendar",
96
+ ".iso" => "application/octet-stream",
97
+ ".jar" => "application/java-archive",
98
+ ".java" => "text/x-java-source",
99
+ ".jnlp" => "application/x-java-jnlp-file",
100
+ ".jpeg" => "image/jpeg",
101
+ ".jpg" => "image/jpeg",
102
+ ".js" => "application/javascript",
103
+ ".json" => "application/json",
104
+ ".log" => "text/plain",
105
+ ".m3u" => "audio/x-mpegurl",
106
+ ".m4v" => "video/mp4",
107
+ ".man" => "text/troff",
108
+ ".mathml" => "application/mathml+xml",
109
+ ".mbox" => "application/mbox",
110
+ ".mdoc" => "text/troff",
111
+ ".me" => "text/troff",
112
+ ".mid" => "audio/midi",
113
+ ".midi" => "audio/midi",
114
+ ".mime" => "message/rfc822",
115
+ ".mml" => "application/mathml+xml",
116
+ ".mng" => "video/x-mng",
117
+ ".mov" => "video/quicktime",
118
+ ".mp3" => "audio/mpeg",
119
+ ".mp4" => "video/mp4",
120
+ ".mp4v" => "video/mp4",
121
+ ".mpeg" => "video/mpeg",
122
+ ".mpg" => "video/mpeg",
123
+ ".ms" => "text/troff",
124
+ ".msi" => "application/x-msdownload",
125
+ ".odp" => "application/vnd.oasis.opendocument.presentation",
126
+ ".ods" => "application/vnd.oasis.opendocument.spreadsheet",
127
+ ".odt" => "application/vnd.oasis.opendocument.text",
128
+ ".ogg" => "application/ogg",
129
+ ".p" => "text/x-pascal",
130
+ ".pas" => "text/x-pascal",
131
+ ".pbm" => "image/x-portable-bitmap",
132
+ ".pdf" => "application/pdf",
133
+ ".pem" => "application/x-x509-ca-cert",
134
+ ".pgm" => "image/x-portable-graymap",
135
+ ".pgp" => "application/pgp-encrypted",
136
+ ".pkg" => "application/octet-stream",
137
+ ".pl" => "text/x-script.perl",
138
+ ".pm" => "text/x-script.perl-module",
139
+ ".png" => "image/png",
140
+ ".pnm" => "image/x-portable-anymap",
141
+ ".ppm" => "image/x-portable-pixmap",
142
+ ".pps" => "application/vnd.ms-powerpoint",
143
+ ".ppt" => "application/vnd.ms-powerpoint",
144
+ ".ps" => "application/postscript",
145
+ ".psd" => "image/vnd.adobe.photoshop",
146
+ ".py" => "text/x-script.python",
147
+ ".qt" => "video/quicktime",
148
+ ".ra" => "audio/x-pn-realaudio",
149
+ ".rake" => "text/x-script.ruby",
150
+ ".ram" => "audio/x-pn-realaudio",
151
+ ".rar" => "application/x-rar-compressed",
152
+ ".rb" => "text/x-script.ruby",
153
+ ".rdf" => "application/rdf+xml",
154
+ ".roff" => "text/troff",
155
+ ".rpm" => "application/x-redhat-package-manager",
156
+ ".rss" => "application/rss+xml",
157
+ ".rtf" => "application/rtf",
158
+ ".ru" => "text/x-script.ruby",
159
+ ".s" => "text/x-asm",
160
+ ".sgm" => "text/sgml",
161
+ ".sgml" => "text/sgml",
162
+ ".sh" => "application/x-sh",
163
+ ".sig" => "application/pgp-signature",
164
+ ".snd" => "audio/basic",
165
+ ".so" => "application/octet-stream",
166
+ ".svg" => "image/svg+xml",
167
+ ".svgz" => "image/svg+xml",
168
+ ".swf" => "application/x-shockwave-flash",
169
+ ".t" => "text/troff",
170
+ ".tar" => "application/x-tar",
171
+ ".tbz" => "application/x-bzip-compressed-tar",
172
+ ".tcl" => "application/x-tcl",
173
+ ".tex" => "application/x-tex",
174
+ ".texi" => "application/x-texinfo",
175
+ ".texinfo" => "application/x-texinfo",
176
+ ".text" => "text/plain",
177
+ ".tif" => "image/tiff",
178
+ ".tiff" => "image/tiff",
179
+ ".torrent" => "application/x-bittorrent",
180
+ ".tr" => "text/troff",
181
+ ".txt" => "text/plain",
182
+ ".vcf" => "text/x-vcard",
183
+ ".vcs" => "text/x-vcalendar",
184
+ ".vrml" => "model/vrml",
185
+ ".war" => "application/java-archive",
186
+ ".wav" => "audio/x-wav",
187
+ ".wma" => "audio/x-ms-wma",
188
+ ".wmv" => "video/x-ms-wmv",
189
+ ".wmx" => "video/x-ms-wmx",
190
+ ".wrl" => "model/vrml",
191
+ ".wsdl" => "application/wsdl+xml",
192
+ ".xbm" => "image/x-xbitmap",
193
+ ".xhtml" => "application/xhtml+xml",
194
+ ".xls" => "application/vnd.ms-excel",
195
+ ".xml" => "application/xml",
196
+ ".xpm" => "image/x-xpixmap",
197
+ ".xsl" => "application/xml",
198
+ ".xslt" => "application/xslt+xml",
199
+ ".yaml" => "text/yaml",
200
+ ".yml" => "text/yaml",
201
+ ".zip" => "application/zip",
202
+ }
203
+ end
204
+ end