Skip to content

Instantly share code, notes, and snippets.

@yous
Created December 5, 2016 10:55
Show Gist options
  • Save yous/40d8e18b33dfb62966c3e1e343f11a96 to your computer and use it in GitHub Desktop.
Save yous/40d8e18b33dfb62966c3e1e343f11a96 to your computer and use it in GitHub Desktop.
Thin + Rack file server with basic auth on Windows
require 'rack'
require 'thin'
dirname = File.expand_path(File.dirname(__FILE__))
fileroot = File.join(dirname, 'files')
module Rack
class UTF8Directory < Directory
class DirectoryBody < Directory::DirectoryBody
private
# Escape single quotes in URL for DIR_FILE
# https://github.com/rack/rack/pull/1131
def DIR_FILE_escape url, *html
[url.gsub("'", '&#x27;'), *html.map { |e| Utils.escape_html(e) }]
end
end
def list_directory(path_info, path, script_name)
files = [['../','Parent Directory','','','']]
path.force_encoding(Encoding::UTF_8)
url_head = (script_name.split('/') + path_info.split('/')).map do |part|
Rack::Utils.escape_path part
end
Dir.entries(path).sort.each do |node|
next if node == '.' || node == '..'
node.encode!(Encoding::UTF_8)
node = ::File.join(path, node)
stat = stat(node)
next unless stat
basename = ::File.basename(node)
ext = ::File.extname(node)
url = ::File.join(*url_head + [Rack::Utils.escape_path(basename)])
size = stat.size
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
size = stat.directory? ? '-' : filesize_format(size)
mtime = stat.mtime.httpdate
url << '/' if stat.directory?
basename << '/' if stat.directory?
files << [ url, basename, size, type, mtime ]
end
return [ 200, { CONTENT_TYPE =>'text/html; charset=utf-8'}, DirectoryBody.new(@root, path, files) ]
end
def list_path(env, path, path_info, script_name)
path.force_encoding(Encoding::UTF_8)
stat = ::File.stat(path)
if stat.readable?
return @app.call(env) if stat.file?
return list_directory(path_info, path, script_name) if stat.directory?
else
raise Errno::ENOENT, 'No such file or directory'
end
rescue Errno::ENOENT, Errno::ELOOP
return entity_not_found(path_info)
end
end
end
Thin::Server.start('0.0.0.0', 8000) do
use Rack::Auth::Basic, 'Protected Area' do |username, password|
username == 'USERNAME' && password == 'PASSWORD'
end
run Rack::UTF8Directory.new(fileroot)
end
@yous
Copy link
Author

yous commented Dec 7, 2016

For Puma, add require 'puma' and replace Thin::Server.start block with

app = Rack::Builder.new do
  use Rack::Auth::Basic, 'Protected Area' do |username, password|
    username == 'USERNAME' && password == 'PASSWORD'
  end

  run Rack::UTF8Directory.new(fileroot)
end
verbose_app = Rack::CommonLogger.new(app, STDOUT)
conf = Puma::Configuration.new do |c|
  c.port 9000, '0.0.0.0'
  c.app verbose_app
end

Puma::Launcher.new(conf, events: Puma::Events.stdio).run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment