feat(middleware): add request logger and static file server
This commit is contained in:
1 parent
a00dd619c1
commit
514e8fada8
2 files changed
+63
No files matched your search
@@ -0,0 +1,18 @@
|
|||||||
|
module Prism
|
||||||
|
module Middleware
|
||||||
|
class Logger
|
||||||
|
def initialize(app, io: $stderr)
|
||||||
|
@app = app
|
||||||
|
@io = io
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
started = Time.now
|
||||||
|
status, headers, body = @app.call(env)
|
||||||
|
elapsed = ((Time.now - started) * 1000).round(2)
|
||||||
|
@io.puts(format('%s %s -> %d (%.2f ms)', env['REQUEST_METHOD'], env['PATH_INFO'], status, elapsed))
|
||||||
|
[status, headers, body]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
module Prism
|
||||||
|
module Middleware
|
||||||
|
class Static
|
||||||
|
ContentTypes = {
|
||||||
|
'.html' => 'text/html;charset=utf-8', '.htm' => 'text/html;charset=utf-8',
|
||||||
|
'.css' => 'text/css', '.js' => 'application/javascript', '.json' => 'application/json',
|
||||||
|
'.png' => 'image/png', '.jpg' => 'image/jpeg', '.jpeg' => 'image/jpeg', '.gif' => 'image/gif',
|
||||||
|
'.svg' => 'image/svg+xml', '.ico' => 'image/x-icon', '.txt' => 'text/plain', '.pdf' => 'application/pdf'
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def initialize(app, root:)
|
||||||
|
@app = app
|
||||||
|
@root = File.expand_path(root)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
method = env['REQUEST_METHOD']
|
||||||
|
return @app.call(env) unless %w[GET HEAD].include?(method)
|
||||||
|
|
||||||
|
file = resolve(env['PATH_INFO'])
|
||||||
|
return @app.call(env) unless file
|
||||||
|
|
||||||
|
serve(file)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def resolve(path_info)
|
||||||
|
return nil if path_info.nil? || path_info.include?("\0")
|
||||||
|
decoded = CGI.unescape(path_info)
|
||||||
|
candidate = File.expand_path(File.join(@root, decoded))
|
||||||
|
return nil unless candidate.start_with?("#{@root}#{File::SEPARATOR}") || candidate == @root
|
||||||
|
return File.join(candidate, 'index.html') if File.directory?(candidate) && File.file?(File.join(candidate, 'index.html'))
|
||||||
|
return nil unless File.file?(candidate)
|
||||||
|
candidate
|
||||||
|
end
|
||||||
|
|
||||||
|
def serve(file)
|
||||||
|
body = File.binread(file)
|
||||||
|
type = ContentTypes.fetch(File.extname(file).downcase, 'application/octet-stream')
|
||||||
|
[200, { 'Content-Type' => type, 'Content-Length' => body.bytesize.to_s }, [body]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user