#!/usr/bin/env ruby # This is free and unencumbered software released into the public domain. # See the `UNLICENSE` file or for more details. require 'open-uri' require 'socket' require 'timeout' require 'webrick' IP_DISCO_SERVICE = 'http://automation.whatismyip.com/n09230945.asp' LAN_IP = '169.254.0.1' PRIVATE_LIST_FILE = '.private' MAX_PORT = 2**16 MAX_DISCOVER_WAIT = 4 # seconds def check_args(dir, port) if !File.directory?(File.expand_path(dir)) puts "WARNING: '#{dir}' is not a directory" end if port > MAX_PORT puts "ERROR: port numer too high, #{port} is > #{MAX_PORT}" exit end end def print_addresses(dir, port) puts "Discovering addresses and hostnames..." begin ip = Timeout::timeout(MAX_DISCOVER_WAIT) do open(IP_DISCO_SERVICE).read end rescue puts "WARNING: Cannot reach IP discovery service" ip = "NOT_AVAILABLE" end begin lan_ip = UDPSocket.open { |s| s.connect(LAN_IP, 1); s.addr[2] } rescue puts "WARNING: Cannot find LAN-local hostname" lan_ip = "NOT_AVAILABLE" end local_uri = "http://localhost:#{port}/" lan_uri = "http://#{lan_ip}:#{port}/" remote_uri = "http://#{ip}:#{port}/" puts "Server starting... (CTRL-C to terminate)" puts puts " local address: <#{local_uri}>" puts " LAN address: <#{lan_uri}>" puts " public address: <#{remote_uri}>" puts end def start_server(dir, port) config = { :DocumentRoot => dir, :Port => port, :DocumentRootOptions => { :FancyIndexing => true }, } private_list_file = File.join(dir, PRIVATE_LIST_FILE) if File.exist?(private_list_file) patterns = File.readlines(private_list_file).map(&:strip) ignored_files = patterns.reject(&:empty?).reject { |f| f.start_with?('#') } ignored_files << PRIVATE_LIST_FILE config[:DocumentRootOptions][:NondisclosureName] = ignored_files end server = WEBrick::HTTPServer.new(config) ['INT', 'TERM'].each { |signal| trap(signal) {server.shutdown} } server.start end if ARGV.count < 2 puts "servedir [DIRECTORY] [PORT]" exit end dir = ARGV[0] || Dir.pwd port = ARGV[1].to_i || 8080 check_args(dir, port) print_addresses(dir, port) start_server(dir, port)