规范化HTTP URI

时间:2015-04-20 19:07:45

标签: ruby uri

我从Akamai的日志文件中获取URI,其中包含以下条目:

/foo/jim/jam
/foo/jim/jam?
/foo/./jim/jam
/foo/bar/../jim/jam
/foo/jim/jam?autho=<randomstring>&file=jam

我想根据规则将所有这些规范化为同一条目:

  • 如果有查询字符串,请从中删除authofile
  • 如果查询字符串为空,请删除尾随?
  • 应删除./的目录条目。
  • 应删除<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个清理路径,我无法全部检查它们。

  • 考虑到可能的日志文件条目,上面是否有任何明显的错误?
  • 有没有更好的方法来实现相同的解析技术,而不是我手工制作的正则表达式?

2 个答案:

答案 0 :(得分:5)

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"

答案 1 :(得分:2)

REALLY 重要的东西,比如, ESSENTIAL 要记住的是,URL / URI是协议,主机,资源的文件路径,然后将选项/参数传递给被引用的资源。 (对于迂腐的人来说,还有其他的,可选的东西,但这已经足够了。)

我们可以通过使用URI类解析它并使用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方法来玩游戏来规范化'。'。和'..'走了,得到一条真正的绝对路径。如果有更多..(父目录)而不是目录链,这将会中断,但是您不应该在提取的路径中找到它,因为这也会破坏服务器/浏览器提供/请求/接收资源的能力。

在处理相对路径时会有点“有趣”,但File仍然是我们的朋友,但这是一个不同的问题。