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.



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

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)



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

attr :cache_control

#cache_controlObject (readonly)

Returns the value of attribute cache_control.



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

def cache_control
  @cache_control
end

#content_typesObject (readonly)

Returns the value of attribute content_types.



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

def content_types
  @content_types
end

#rootObject (readonly)

Returns the value of attribute root.



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

def root
  @root
end

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



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

attr :root

Instance Method Details

#call(request) ⇒ Object

Handle an incoming HTTP request.



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

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.



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

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.



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

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



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

attr :content_types

#response_for(path) ⇒ Object

Generate an HTTP response for the given file path.



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

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