规范化 HTTP URI
Normalize HTTP URI
我从 Akamai 的日志文件中获取 URI,其中包含如下条目:
/foo/jim/jam
/foo/jim/jam?
/foo/./jim/jam
/foo/bar/../jim/jam
/foo/jim/jam?autho=<randomstring>&file=jam
我想将所有这些规范化为同一个条目,规则如下:
- 如果有查询字符串,从中删除
autho
和 file
。
- 如果查询字符串为空,删除尾随
?
.
- 应删除
./
的目录条目。
- 应删除
<fulldir>/../
的目录条目。
我原以为 Ruby 的 URI
库会涵盖这个,但是:
- 它不提供任何解析部分查询字符串的机制。 (并不是说这很难做到,也不是标准。)
如果查询字符串为空,它不会删除尾随 ?
。
URI.parse('/foo?jim').tap{ |u| u.query='' }.to_s #=> "/foo?"
normalize
方法不会清理路径中的 .
或 ..
。
所以,在官方库失败的情况下,我发现自己正在编写一个基于正则表达式的解决方案。
def normalize(path)
result = path.dup
path.sub! /(?<=\?).+$/ do |query|
query.split('&').reject do |kv|
%w[ autho file ].include?(kv[/^[^=]+/])
end.join('&')
end
path.sub! /\?$/, ''
path.sub!(/^[^?]+/){ |path| path.gsub(%r{[^/]+/\.\.},'').gsub('/./','/') }
end
它恰好适用于我上面列出的测试用例,但有 450,000 条路径需要清理,我无法手动检查它们。
- 考虑到可能的日志文件条目,上述是否有任何明显的错误?
- 有没有更好的方法来完成同样的事情,依靠经过验证的解析技术而不是我的手工正则表达式?
addressable gem 将为您规范化这些:
require 'addressable/uri'
# normalize relative paths
uri = Addressable::URI.parse('http://example.com/foo/bar/../jim/jam')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
# removes trailing ?
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
# leaves empty parameters alone
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?jim')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam?jim"
# remove specific query parameters
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?autho=<randomstring>&file=jam')
cleaned_query = uri.query_values
cleaned_query.delete('autho')
cleaned_query.delete('file')
uri.query_values = cleaned_query
uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
真正重要的东西,比如ESSENTIAL要记住的是,URL/URI是一个协议,一个host,资源的文件路径,后跟 options/parameters 被传递给被引用的资源。 (对于迂腐的人来说,那里还有其他可选的东西,但这就足够了。)
我们可以通过使用 URI class, and using the path
方法解析 URL 来提取路径。一旦我们有了路径,我们就会有一个绝对路径或一个基于站点根目录的相对路径。处理绝对路径很简单:
require 'uri'
%w[
/foo/jim/jam
/foo/jim/jam?
/foo/./jim/jam
/foo/bar/../jim/jam
/foo/jim/jam?autho=<randomstring>&file=jam
].each do |url|
uri = URI.parse(url)
path = uri.path
puts File.absolute_path(path)
end
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
因为路径是基于服务器根目录的文件路径,我们玩游戏可以使用Ruby的File.absolute_path
方法将'.'归一化。和'..'离开并获得真正的绝对路径。如果 ..
(父目录)多于目录链,这将中断,但您不应该在提取的路径中找到它,因为这也会破坏 server/browser 到 serve/request/receive 的能力资源。
处理相对路径时 "interesting" 会多一些,但 File 仍然是我们的朋友,但这是一个不同的问题。
我从 Akamai 的日志文件中获取 URI,其中包含如下条目:
/foo/jim/jam
/foo/jim/jam?
/foo/./jim/jam
/foo/bar/../jim/jam
/foo/jim/jam?autho=<randomstring>&file=jam
我想将所有这些规范化为同一个条目,规则如下:
- 如果有查询字符串,从中删除
autho
和file
。 - 如果查询字符串为空,删除尾随
?
. - 应删除
./
的目录条目。 - 应删除
<fulldir>/../
的目录条目。
我原以为 Ruby 的 URI
库会涵盖这个,但是:
- 它不提供任何解析部分查询字符串的机制。 (并不是说这很难做到,也不是标准。)
如果查询字符串为空,它不会删除尾随
?
。URI.parse('/foo?jim').tap{ |u| u.query='' }.to_s #=> "/foo?"
normalize
方法不会清理路径中的.
或..
。
所以,在官方库失败的情况下,我发现自己正在编写一个基于正则表达式的解决方案。
def normalize(path)
result = path.dup
path.sub! /(?<=\?).+$/ do |query|
query.split('&').reject do |kv|
%w[ autho file ].include?(kv[/^[^=]+/])
end.join('&')
end
path.sub! /\?$/, ''
path.sub!(/^[^?]+/){ |path| path.gsub(%r{[^/]+/\.\.},'').gsub('/./','/') }
end
它恰好适用于我上面列出的测试用例,但有 450,000 条路径需要清理,我无法手动检查它们。
- 考虑到可能的日志文件条目,上述是否有任何明显的错误?
- 有没有更好的方法来完成同样的事情,依靠经过验证的解析技术而不是我的手工正则表达式?
addressable gem 将为您规范化这些:
require 'addressable/uri'
# normalize relative paths
uri = Addressable::URI.parse('http://example.com/foo/bar/../jim/jam')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
# removes trailing ?
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
# leaves empty parameters alone
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?jim')
puts uri.normalize.to_s #=> "http://example.com/foo/jim/jam?jim"
# remove specific query parameters
uri = Addressable::URI.parse('http://example.com/foo/jim/jam?autho=<randomstring>&file=jam')
cleaned_query = uri.query_values
cleaned_query.delete('autho')
cleaned_query.delete('file')
uri.query_values = cleaned_query
uri.normalize.to_s #=> "http://example.com/foo/jim/jam"
真正重要的东西,比如ESSENTIAL要记住的是,URL/URI是一个协议,一个host,资源的文件路径,后跟 options/parameters 被传递给被引用的资源。 (对于迂腐的人来说,那里还有其他可选的东西,但这就足够了。)
我们可以通过使用 URI class, and using the path
方法解析 URL 来提取路径。一旦我们有了路径,我们就会有一个绝对路径或一个基于站点根目录的相对路径。处理绝对路径很简单:
require 'uri'
%w[
/foo/jim/jam
/foo/jim/jam?
/foo/./jim/jam
/foo/bar/../jim/jam
/foo/jim/jam?autho=<randomstring>&file=jam
].each do |url|
uri = URI.parse(url)
path = uri.path
puts File.absolute_path(path)
end
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
# >> /foo/jim/jam
因为路径是基于服务器根目录的文件路径,我们玩游戏可以使用Ruby的File.absolute_path
方法将'.'归一化。和'..'离开并获得真正的绝对路径。如果 ..
(父目录)多于目录链,这将中断,但您不应该在提取的路径中找到它,因为这也会破坏 server/browser 到 serve/request/receive 的能力资源。
处理相对路径时 "interesting" 会多一些,但 File 仍然是我们的朋友,但这是一个不同的问题。