Class: Stipa::Middleware::Static

Inherits:
Object
  • Object
show all
Defined in:
lib/stipa/static.rb

Overview

Serve static files from a directory (typically ‘public/’).

Features:

- Path traversal prevention (no ../../ escaping the root)
- Correct MIME types for web assets including .vue and .mjs files
- ETag-based conditional GET (304 Not Modified)
- HEAD request support
- Only intercepts GET/HEAD; other methods fall through to the app

Usage:

app.use Stipa::Middleware::Static, root: 'public'
app.use Stipa::Middleware::Static, root: '/srv/myapp/public', prefix: '/assets'

Constant Summary collapse

MIME_TYPES =
{
  '.html'  => 'text/html; charset=utf-8',
  '.css'   => 'text/css; charset=utf-8',
  '.js'    => 'application/javascript; charset=utf-8',
  '.mjs'   => 'application/javascript; charset=utf-8',
  # .vue files are served as JS so the browser can import them as ES modules
  '.vue'   => 'application/javascript; charset=utf-8',
  '.json'  => 'application/json; charset=utf-8',
  '.png'   => 'image/png',
  '.jpg'   => 'image/jpeg',
  '.jpeg'  => 'image/jpeg',
  '.gif'   => 'image/gif',
  '.webp'  => 'image/webp',
  '.svg'   => 'image/svg+xml',
  '.ico'   => 'image/x-icon',
  '.woff'  => 'font/woff',
  '.woff2' => 'font/woff2',
  '.ttf'   => 'font/ttf',
  '.eot'   => 'application/vnd.ms-fontobject',
  '.map'   => 'application/json',
  '.txt'   => 'text/plain; charset=utf-8',
  '.xml'   => 'application/xml; charset=utf-8',
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(next_app, root:, prefix: '/') ⇒ Static

root: directory to serve files from (absolute or relative to cwd) prefix: URL path prefix that triggers static file serving (default ‘/’)



42
43
44
45
46
# File 'lib/stipa/static.rb', line 42

def initialize(next_app, root:, prefix: '/')
  @next_app = next_app
  @root   = File.expand_path(root)
  @prefix = prefix.chomp('/')
end

Instance Method Details

#call(req, res) ⇒ Object



48
49
50
51
52
53
54
55
# File 'lib/stipa/static.rb', line 48

def call(req, res)
  if %w[GET HEAD].include?(req.method) && req.path.start_with?("#{@prefix}/", @prefix)
    rel = req.path.delete_prefix(@prefix)
    found = serve_static(rel, req, res)
    return found if found
  end
  @next_app.call(req, res)
end