Class: Utopia::Static

Inherits:
Object
  • Object
show all
Defined in:
lib/utopia/static.rb,
lib/utopia/static/local_file.rb,
lib/utopia/static/mime_types.rb

Overview

A middleware which serves static files from the specified root directory.

Defined Under Namespace

Classes: LocalFile, MimeTypeLoader

Constant Summary collapse

DEFAULT_CACHE_CONTROL =
'public, max-age=3600'.freeze
LAST_MODIFIED =
'Last-Modified'.freeze
CONTENT_TYPE =
HTTP::CONTENT_TYPE
CACHE_CONTROL =
HTTP::CACHE_CONTROL
ETAG =
'ETag'.freeze
ACCEPT_RANGES =
'Accept-Ranges'.freeze
MIME_TYPES =

Default mime-types which are common for files served over HTTP:

{
	: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",
	},
	:media => [
		:xiph, "mp3", "mp4", "wav", "aiff", ["aac", "audio/x-aac"], "mov", "avi", "wmv", "mpg"
	],
	:text => [
		"html", "css", "js", ["map", "application/json"], "txt", "rtf", "xml", "pdf"
	],
	:fonts => [
		"otf", ["eot", "application/vnd.ms-fontobject"], "ttf", "woff", "woff2"
	],
	:archive => [
		"zip", "tar", "tgz", "tar.gz", "tar.bz2", ["dmg", "application/x-apple-diskimage"],
		["torrent", "application/x-bittorrent"]
	],
	:images => [
		"png", "gif", "jpeg", "tiff", "svg", "webp"
	],
	:default => [
		:media, :text, :archive, :images, :fonts
	]
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, root: Utopia::default_root, types: MIME_TYPES[:default], cache_control: DEFAULT_CACHE_CONTROL) ⇒ Static

Returns a new instance of Static.

Parameters:

  • root (String) (defaults to: Utopia::default_root)

    The root directory to serve files from.

  • types (Array) (defaults to: MIME_TYPES[:default])

    The mime-types (and file extensions) to recognize/serve.

  • cache_control (String) (defaults to: DEFAULT_CACHE_CONTROL)

    The cache-control header to set for static content.



22
23
24
25
26
27
28
29
# File 'lib/utopia/static.rb', line 22

def initialize(app, root: Utopia::default_root, types: MIME_TYPES[:default], cache_control: DEFAULT_CACHE_CONTROL)
	@app = app
	@root = root
	
	@extensions = MimeTypeLoader.extensions_for(types)
	
	@cache_control = cache_control
end

Instance Attribute Details

#extensionsObject (readonly)

Returns the value of attribute extensions.



52
53
54
# File 'lib/utopia/static.rb', line 52

def extensions
  @extensions
end

Instance Method Details

#call(env) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/utopia/static.rb', line 84

def call(env)
	path_info = env[Rack::PATH_INFO]
	extension = File.extname(path_info)
	
	if @extensions.key?(extension.downcase)
		if response = self.respond(env, path_info, extension)
			return response
		end
	end
	
	# else if no file was found:
	return @app.call(env)
end

#fetch_file(path) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/utopia/static.rb', line 41

def fetch_file(path)
	# We need file_path to be an absolute path for X-Sendfile to work correctly.
	file_path = File.join(@root, path.components)
	
	if File.exist?(file_path)
		return LocalFile.new(@root, path)
	else
		return nil
	end
end

#freezeObject



31
32
33
34
35
36
37
38
39
# File 'lib/utopia/static.rb', line 31

def freeze
	return self if frozen?
	
	@root.freeze
	@extensions.freeze
	@cache_control.freeze
	
	super
end

#respond(env, path_info, extension) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/utopia/static.rb', line 60

def respond(env, path_info, extension)
	path = Path[path_info].simplify
	
	if locale = env[Localization::CURRENT_LOCALE_KEY]
		path.last.insert(path.last.rindex('.') || -1, ".#{locale}")
	end
	
	if file = fetch_file(path)
		response_headers = {
			LAST_MODIFIED => file.mtime_date,
			CONTENT_TYPE => @extensions[extension],
			CACHE_CONTROL => @cache_control,
			ETAG => file.etag,
			ACCEPT_RANGES => "bytes"
		}
		
		if file.modified?(env)
			return file.serve(env, response_headers)
		else
			return [304, response_headers, []]
		end
	end
end