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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
# frozen_string_literal: true
require_relative 'helper'
require 'logger'
separate_testing do
require_relative '../lib/rack/common_logger'
require_relative '../lib/rack/lint'
require_relative '../lib/rack/mock_request'
end
describe Rack::CommonLogger do
obj = 'foobar'
length = obj.size
app = Rack::Lint.new lambda { |env|
[200,
{ "content-type" => "text/html", "content-length" => length.to_s },
[obj]]}
app_without_length = Rack::Lint.new lambda { |env|
[200,
{ "content-type" => "text/html" },
[]]}
app_with_zero_length = Rack::Lint.new lambda { |env|
[200,
{ "content-type" => "text/html", "content-length" => "0" },
[]]}
app_without_lint = lambda { |env|
[200,
{ "content-type" => "text/html", "content-length" => length.to_s },
[obj]]}
it "log to rack.errors by default" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
res.errors.wont_be :empty?
res.errors.must_match(/"GET \/ HTTP\/1\.1" 200 #{length} /)
end
it "log to anything with +write+" do
log = StringIO.new
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
log.string.must_match(/"GET \/ HTTP\/1\.1" 200 #{length} /)
end
it "work with standard library logger" do
logdev = StringIO.new
log = Logger.new(logdev)
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
logdev.string.must_match(/"GET \/ HTTP\/1\.1" 200 #{length} /)
end
it "log - content length if header is missing" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
res.errors.wont_be :empty?
res.errors.must_match(/"GET \/ HTTP\/1\.1" 200 - /)
end
it "log - content length if header is zero" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
res.errors.wont_be :empty?
res.errors.must_match(/"GET \/ HTTP\/1\.1" 200 - /)
end
it "log - records host from X-Forwarded-For header" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/", 'HTTP_X_FORWARDED_FOR' => '203.0.113.0')
res.errors.wont_be :empty?
res.errors.must_match(/203\.0\.113\.0 - /)
end
it "log - records host from RFC 7239 forwarded for header" do
res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/", 'HTTP_FORWARDED' => 'for=203.0.113.0')
res.errors.wont_be :empty?
res.errors.must_match(/203\.0\.113\.0 - /)
end
def with_mock_time(t = 0)
mc = class << Time; self; end
mc.send :alias_method, :old_now, :now
mc.send :define_method, :now do
at(t)
end
yield
ensure
mc.send :undef_method, :now
mc.send :alias_method, :now, :old_now
end
it "log in common log format" do
log = StringIO.new
with_mock_time do
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/", 'QUERY_STRING' => 'foo=bar')
end
md = /- - - \[([^\]]+)\] "(\w+) \/\?foo=bar HTTP\/1\.1" (\d{3}) \d+ ([\d\.]+)/.match(log.string)
md.wont_equal nil
time, method, status, duration = *md.captures
time.must_equal Time.at(0).strftime("%d/%b/%Y:%H:%M:%S %z")
method.must_equal "GET"
status.must_equal "200"
(0..1).must_include duration.to_f
end
it "escapes non printable characters including newline" do
logdev = StringIO.new
log = Logger.new(logdev)
Rack::MockRequest.new(Rack::CommonLogger.new(app_without_lint, log)).request("GET\x1f", "/hello")
logdev.string.must_match(/GET\\x1f \/hello HTTP\/1\.1/)
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/", 'REMOTE_USER' => "foo\nbar", "QUERY_STRING" => "bar\nbaz")
logdev.string[-1].must_equal "\n"
logdev.string.must_include("foo\\xabar")
logdev.string.must_include("bar\\xabaz")
end
it "log path with PATH_INFO" do
logdev = StringIO.new
log = Logger.new(logdev)
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/hello")
logdev.string.must_match(/"GET \/hello HTTP\/1\.1" 200 #{length} /)
end
it "log path with SCRIPT_NAME" do
logdev = StringIO.new
log = Logger.new(logdev)
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/path", script_name: "/script")
logdev.string.must_match(/"GET \/script\/path HTTP\/1\.1" 200 #{length} /)
end
it "log path with SERVER_PROTOCOL" do
logdev = StringIO.new
log = Logger.new(logdev)
Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/path", http_version: "HTTP/1.0")
logdev.string.must_match(/"GET \/path HTTP\/1\.0" 200 #{length} /)
end
def length
123
end
def self.obj
"hello world"
end
end
|