Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion guides/middleware/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The {ruby Utopia::Static} middleware services static files efficiently and suppo
use Utopia::Static,
# The root path to serve files from:
root: "path/to/root",
# The mime-types to recognize/serve:
# The file extension groups to recognize/serve:
types: [:default, :xiph],
# Cache-Control header for files:
cache_control: 'public, max-age=7200'
Expand Down
2 changes: 1 addition & 1 deletion lib/utopia/static/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Middleware < Protocol::HTTP::Middleware
include Localization::Resolver

# @param root [String] The root directory to serve files from.
# @param types [Array] The mime-types (and file extensions) to recognize/serve.
# @param types [Array] The file extensions and named groups to recognize/serve.
# @param cache_control [String] The cache-control header to set for static content.
def initialize(app, root: Utopia::default_root, types: MIME_TYPES[:default], cache_control: DEFAULT_CACHE_CONTROL)
super(app)
Expand Down
61 changes: 28 additions & 33 deletions lib/utopia/static/mime_types.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,29 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2017-2025, by Samuel Williams.
# Copyright, 2017-2026, by Samuel Williams.

require "mime/types"
require "protocol/media/registry"

module Utopia
# A middleware which serves static files from the specified root directory.
module Static
# Default mime-types which are common for files served over HTTP:
MIME_TYPES = {
:xiph => {
"ogx" => "application/ogg",
"ogv" => "video/ogg",
"oga" => "audio/ogg",
"ogg" => "audio/ogg",
"spx" => "audio/ogg",
"flac" => "audio/flac",
"anx" => "application/annodex",
"axa" => "audio/annodex",
"xspf" => "application/xspf+xml",
},
:xiph => [
"ogx", "ogv", "oga", "ogg", "spx", "flac", "xspf"
],
:media => [
:xiph, "mp3", "mp4", "wav", "aiff", "aac", "webm", "mov", "avi", "wmv", "mpg", "m3u8", "ts"
:xiph, "mp3", "mp4", "wav", "aiff", "aac", "webm", "weba", "mov", "avi", "wmv", "mpg", "m3u8", "ts"
],
:text => [
"html", "css", "js", ["map", "application/json"], "txt", "rtf", "xml", "pdf"
"html", "css", "js", "map", "txt", "rtf", "xml", "pdf"
],
:fonts => [
"otf", ["eot", "application/vnd.ms-fontobject"], "ttf", "woff", "woff2"
"otf", "eot", "ttf", "woff", "woff2"
],
:archive => [
"zip", "tar", "tgz", "tar.gz", "tar.bz2", ["dmg", "application/x-apple-diskimage"],
["torrent", "application/x-bittorrent"]
"zip", "tar", "tgz", "gz", "bz2", "dmg", "torrent"
],
:images => [
"png", "gif", "jpeg", "tiff", "svg", "webp"
Expand Down Expand Up @@ -63,39 +54,43 @@ def self.extensions_for(types, library = MIME_TYPES)
return loader.extensions
end

# Add extensions from non-obsolete MIME type records.
# @parameter mime_types [Array(MIME::Type)] The MIME type records.
# @returns [Array(MIME::Type)] The supplied MIME type records.
def extract_extensions(mime_types)
mime_types.select{|mime_type| !mime_type.obsolete?}.each do |mime_type|
mime_type.extensions.each do |ext|
@extensions["." + ext] ||= mime_type.content_type
end
# Add an extension using its registered media type.
# @parameter extension [String] The file extension.
# @returns [Protocol::Media::Registry::Record] The registered media type record.
def add_extension(extension)
# Normalize optional leading dots and case before constructing the File.extname-style key:
extension = extension.delete_prefix(".").downcase

if record = Protocol::Media::Registry.for_extension(extension)
@extensions["." + extension] = record.type.to_s
return record
end

raise ExpansionError.new("Unknown file extension: #{extension.inspect}")
end

# Raised when expansion processing fails.
class ExpansionError < ArgumentError
end

# Expand named groups, extension pairs, file names, patterns, and MIME records into extension mappings.
# Expand named groups, file extensions, and explicit extension pairs into extension mappings.
# @parameter types [Array] The MIME type definitions.
# @returns [Array] The supplied definitions.
# @raises [ExpansionError] If a definition cannot be expanded.
def expand(types)
types.each do |type|
case type
when Symbol
self.expand(MIME_TYPES[type])
self.expand(@library.fetch(type))
when Array
@extensions["." + type[0]] = type[1]
when String
self.extract_extensions MIME::Types.of(type)
when Regexp
self.extract_extensions MIME::Types[type]
when MIME::Type
self.extract_extensions.call([type])
self.add_extension(type)
else
raise ExpansionError.new("Unsupported MIME type definition: #{type.inspect}")
end
rescue ExpansionError
raise
rescue
raise ExpansionError.new("#{self.class.name}: Error while processing #{type.inspect}!")
end
Expand Down
2 changes: 2 additions & 0 deletions test/utopia/static.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
".txt" => be == "text/plain",
".webm" => be == "video/webm",
".weba" => be == "audio/webm",
".ogg" => be == "audio/vorbis",
".spx" => be == "audio/speex",
".html" => be == "text/html",
)
end
Expand Down
2 changes: 1 addition & 1 deletion utopia.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ Gem::Specification.new do |spec|
spec.add_dependency "console", "~> 1.24"
spec.add_dependency "irb"
spec.add_dependency "mail", "~> 2.6"
spec.add_dependency "mime-types", "~> 3.0"
spec.add_dependency "msgpack"
spec.add_dependency "net-smtp"
spec.add_dependency "protocol-content", "~> 0.1"
spec.add_dependency "protocol-http", "~> 0.70"
spec.add_dependency "protocol-media", "~> 0.3"
spec.add_dependency "protocol-media-registry", "~> 0.1"
spec.add_dependency "protocol-url", "~> 0.10"
spec.add_dependency "samovar", "~> 2.1"
spec.add_dependency "traces", "~> 0.10"
Expand Down
Loading