Class: Lively::Assets

Inherits:
Protocol::HTTP::Middleware
  • Object
show all
Defined in:
lib/lively/assets.rb

Overview

Represents an HTTP middleware for serving static assets.

This middleware serves static files from a configured root directory with appropriate content type headers and caching controls. It supports a wide range of web file formats including HTML, CSS, JavaScript, images, fonts, audio, video, and WebAssembly files.

Constant Summary collapse

DEFAULT_CACHE_CONTROL =
"no-store, no-cache, must-revalidate, max-age=0"
DEFAULT_CONTENT_TYPES =
{
	".html" => "text/html",
	".css" => "text/css",
	".js" => "application/javascript",
	".mjs" => "application/javascript",
	".json" => "application/json",
	".png" => "image/png",
	".jpg" => "image/jpeg",
	".jpeg" => "image/jpeg",
	".gif" => "image/gif",
	".webp" => "image/webp",
	".svg" => "image/svg+xml",
	".ico" => "image/x-icon",
	".mp3" => "audio/mpeg",
	".wav" => "audio/wav",
	".ogg" => "audio/ogg",
	".mp4" => "video/mp4",
	".webm" => "video/webm",
	".ttf" => "font/ttf",
	".woff" => "font/woff",
	".woff2" => "font/woff2",
	".wasm" => "application/wasm",
}
PUBLIC_ROOT =
File.expand_path("../../public", __dir__)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(delegate, root: PUBLIC_ROOT, content_types: DEFAULT_CONTENT_TYPES, cache_control: DEFAULT_CACHE_CONTROL) ⇒ Assets

Initialize a new assets middleware instance.



52
53
54
55
56
57
58
59
# File 'lib/lively/assets.rb', line 52

def initialize(delegate, root: PUBLIC_ROOT, content_types: DEFAULT_CONTENT_TYPES, cache_control: DEFAULT_CACHE_CONTROL)
	super(delegate)
	
	@root = File.expand_path(root)
	
	@content_types = content_types
	@cache_control = cache_control
end

Instance Attribute Details

#Cache control header value for asset responses.(controlheadervalue) ⇒ Object (readonly)



68
# File 'lib/lively/assets.rb', line 68

attr :cache_control

#cache_controlObject (readonly)

Returns the value of attribute cache_control.



68
69
70
# File 'lib/lively/assets.rb', line 68

def cache_control
  @cache_control
end

#content_typesObject (readonly)

Returns the value of attribute content_types.



65
66
67
# File 'lib/lively/assets.rb', line 65

def content_types
  @content_types
end

#rootObject (readonly)

Returns the value of attribute root.



62
63
64
# File 'lib/lively/assets.rb', line 62

def root
  @root
end

#The absolute path to the root directory for serving assets.(absolutepathtotherootdirectory) ⇒ Object (readonly)



62
# File 'lib/lively/assets.rb', line 62

attr :root

Instance Method Details

#call(request) ⇒ Object

Handle an incoming HTTP request.



120
121
122
123
124
125
126
# File 'lib/lively/assets.rb', line 120

def call(request)
	if path = expand_path(request.path)
		return response_for(path)
	end
	
	super
end

#expand_path(path) ⇒ Object

Expand a relative path to an absolute path within the asset root.



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/lively/assets.rb', line 104

def expand_path(path)
	path = path.split("/").map(&URI.method(:decode_uri_component))
	
	root = File.realpath(@root)
	path = File.realpath(File.join(@root, path))
	
	if path.start_with?(root) && File.file?(path)
		return path
	end
rescue Errno::ENOENT
	nil
end

#freezeObject

Freeze this middleware instance and all its configuration.



72
73
74
75
76
77
78
79
80
# File 'lib/lively/assets.rb', line 72

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

#Mapping of file extensions to content types.=(offileextensionstocontenttypes. = (value)) ⇒ Object



65
# File 'lib/lively/assets.rb', line 65

attr :content_types

#response_for(path) ⇒ Object

Generate an HTTP response for the given file path.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/lively/assets.rb', line 85

def response_for(path)
	extension = File.extname(path)
	
	if content_type = @content_types[extension]
		headers = [
			["content-type", content_type],
			["cache-control", @cache_control],
		]
		
		return Protocol::HTTP::Response[200, headers, Protocol::HTTP::Body::File.open(path)]
	else
		Console.warn(self, "Unsupported media type!", path: path)
		return Protocol::HTTP::Response[415, [["content-type", "text/plain"]], "Unsupported media type: #{extension.inspect}!"]
	end
end