class Glim::LocalServer::Servlet
Public Class Methods
new(server, config)
click to toggle source
# File lib/local_server.rb, line 119 def initialize(server, config) @config = config end
Public Instance Methods
content_for_file(file)
click to toggle source
# File lib/local_server.rb, line 170 def content_for_file(file) if file.frontmatter? begin file.output rescue Glim::Error => e content = "<pre>#{e.messages.join("\n")}</pre>" self.create_page("Error", "Exception raised for <code>#{file}</code>", content) rescue => e content = "<pre>#{e.to_s}</pre>" self.create_page("Error", "Exception raised for <code>#{file}</code>", content) end else File.read(file.path) end end
create_page(title, heading, content)
click to toggle source
# File lib/local_server.rb, line 278 def create_page(title, heading, content) <<~HTML <style>body { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif; } </style> <title>#{title}</title> <h1>#{heading}</h1> #{content} HTML end
directory_index_for_path(path)
click to toggle source
# File lib/local_server.rb, line 209 def directory_index_for_path(path) candidates = self.files.map { |file| file.output_path('/') } candidates = candidates.select { |candidate| path_descends_from?(candidate, path) } candidates = candidates.map { |candidate| candidate.sub(/(^#{Regexp.escape(path.chomp('/'))}\/[^\/]+\/?).*/, '\1') }.sort.uniq candidates.unshift(path + '/..') if path != '/' heading = "Index of <code>#{path}</code>" content = candidates.map do |candidate| "<li><a href = '#{candidate}'>#{candidate.sub(/.*?([^\/]+\/?)$/, '\1')}</a></li>" end self.create_page("Directory Index", heading, "<ul>#{content.join("\n")}</ul>") end
do_GET(request, response)
click to toggle source
# File lib/local_server.rb, line 123 def do_GET(request, response) @@mutex.synchronize do do_GET_impl(request, response) end end
do_GET_impl(request, response)
click to toggle source
# File lib/local_server.rb, line 129 def do_GET_impl(request, response) status, mime_type, body, file = 200, nil, nil, nil if request.path == '/.ws/script.js' mime_type, body = self.mime_type_for(request.path), self.websocket_script elsif page = self.find_page(request.path) file = page elsif dir = self.find_directory(request.path) if request.path.end_with?('/') if request.path == '/' || @config['show_dir_listing'] mime_type, body = 'text/html', self.directory_index_for_path(dir) else $log.warn("Directory index forbidden for: #{request.path}") status = 403 end else response['Location'] = "#{dir}/" status = 302 end else $log.warn("No file for request: #{request.path}") status = 404 end if status != 200 && body.nil? && file.nil? unless file = self.find_error_page(status, request.path) mime_type, body = 'text/html', self.error_page_for_status(status, request.path) end end mime_type ||= file ? self.mime_type_for(file.output_path('/')) : 'text/plain' body ||= content_for_file(file) response['Cache-Control'] = 'no-cache, no-store, must-revalidate' response['Pragma'] = 'no-cache' response['Expires'] = '0' response.status = status response.content_type = mime_type response.body = mime_type.start_with?('text/html') ? inject_reload_script(body) : body end
error_page_for_status(status, path)
click to toggle source
# File lib/local_server.rb, line 223 def error_page_for_status(status, path) case status when 302 then title, heading, content = "302 Redirecting…", "Redirecting…", "Your browser should have redirected you." when 403 then title, heading, content = "403 Forbidden", "Forbidden", "You don't have permission to access <code>#{path}</code> on this server." when 404 then title, heading, content = "404 Not Found", "Not Found", "The requested URL <code>#{path}</code> was not found on this server." else title, heading, content = "Error #{status}", "Error #{status}", "No detailed description of this error." end self.create_page(title, heading, content) end
files()
click to toggle source
# File lib/local_server.rb, line 303 def files @config.site.files_and_documents.select { |file| file.write? } end
find_directory(path)
click to toggle source
# File lib/local_server.rb, line 204 def find_directory(path) path = path.chomp('/') unless path == '/' self.files.map { |file| File.dirname(file.output_path('/')) }.find { |dir| path == dir } end
find_error_page(status, path)
click to toggle source
# File lib/local_server.rb, line 197 def find_error_page(status, path) candidates = self.files.select do |file| file.basename == status.to_s && path_descends_from?(path, File.dirname(file.output_path('/'))) end candidates.max { |lhs, rhs| lhs.output_path('/').size <=> rhs.output_path('/').size } end
find_page(path)
click to toggle source
# File lib/local_server.rb, line 186 def find_page(path) self.files.find do |file| candidate = file.output_path('/') if path == candidate || path + File.extname(candidate) == candidate true elsif path.end_with?('/') File.basename(candidate, '.*') == 'index' && path + File.basename(candidate) == candidate end end end
inject_reload_script(content)
click to toggle source
# File lib/local_server.rb, line 290 def inject_reload_script(content) return content unless @config['livereload'] script_tag = "<script src='#{@config['url']}/.ws/script.js'></script>" if content =~ /<head.*?>/ content = "#$`#$&#{script_tag}#$'" elsif content =~ /<html.*?>/ content = "#$`#$&#{script_tag}#$'" else content = script_tag + content end end
mime_type_for(filename, encoding = nil)
click to toggle source
# File lib/local_server.rb, line 307 def mime_type_for(filename, encoding = nil) if type = MIME::Types.type_for(filename).shift if type.ascii? || type.media_type == 'text' || %w( ecmascript javascript ).include?(type.sub_type) "#{type.content_type}; charset=#{encoding || @config['encoding']}" else type.content_type end else 'application/octet-stream' end end
path_descends_from?(path, parent)
click to toggle source
# File lib/local_server.rb, line 274 def path_descends_from?(path, parent) parent == '/' || path[parent.chomp('/').size] == '/' && path.start_with?(parent) end
websocket_script()
click to toggle source
# File lib/local_server.rb, line 233 def websocket_script <<~JS const glim = { connect: function (host, port, should_retry, should_reload) { const server = host + ":" + port console.log("Connecting to Glim’s live reload server (" + server + ")…"); const socket = new WebSocket("ws://" + server + "/socket"); socket.onopen = () => { console.log("Established connection: Live reload enabled.") if(should_reload) { document.location.reload(true); } }; socket.onmessage = (event) => { console.log("Message from live reload server: " + event.data); if(event.data == 'reload') { document.location.reload(true); } else if(event.data == 'close') { window.close(); } }; socket.onclose = () => { console.log("Lost connection: Live reload disabled.") if(should_retry) { window.setTimeout(() => this.connect(host, port, should_retry, true), 2500); } }; }, }; glim.connect('#{@config['host']}', #{@config['livereload_port']}, true /* should_retry */, false /* should_reload */); JS end