1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
# frozen_string_literal: true
module HTMLProofer
class Middleware
include HTMLProofer::Utils
class InvalidHtmlError < StandardError
def initialize(failures)
super
@failures = failures
end
def message
"HTML Validation errors (skip by adding `?proofer-ignore` to URL): \n#{@failures.join("\n")}"
end
end
def self.options
@options ||= {
type: :file,
allow_missing_href: true, # Permitted in html5
allow_hash_href: true,
check_external_hash: true,
check_html: true,
url_ignore: [%r{^/}], # Don't try to check if local files exist
validation: { report_eof_tags: true }
}
end
def initialize(app)
@app = app
end
HTML_SIGNATURE = [
'<!DOCTYPE HTML',
'<HTML',
'<HEAD',
'<SCRIPT',
'<IFRAME',
'<H1',
'<DIV',
'<FONT',
'<TABLE',
'<A',
'<STYLE',
'<TITLE',
'<B',
'<BODY',
'<BR',
'<P',
'<!--'
].freeze
def call(env)
result = @app.call(env)
return result if env['REQUEST_METHOD'] != 'GET'
return result if /proofer-ignore/.match?(env['QUERY_STRING'])
return result if result.first != 200
body = []
result.last.each { |e| body << e }
body = body.join
begin
html = body.lstrip
rescue StandardError
return result # Invalid encoding; it's not gonna be html.
end
if HTML_SIGNATURE.any? { |sig| html.upcase.start_with? sig }
parsed = HTMLProofer::Runner.new(
'response',
Middleware.options
).check_parsed(
Nokogiri::HTML5(html, max_errors: -1), 'response'
)
raise InvalidHtmlError, parsed[:failures] unless parsed[:failures].empty?
end
result
end
end
end
|