提交 a6179257 编写于 作者: A Andrew White

Optimize URI escaping

The URI::Parser#escape method is a general use method that has to deal
with a variety of input however our use of it is limited in scope so
we can increase the performance by implementing our specific needs
within ActionDispatch::Journey::Router::Utils directly.

If there is no encoding required then there is no change in performance
or number of objects allocated, but for each character that needs to be
encoded we save five object allocations and gain a performance boost.
The performance boost seen varies from 20% when there is one character
to over 50% when encoding ten characters.
上级 e2ef83f8
require 'uri'
module ActionDispatch module ActionDispatch
module Journey # :nodoc: module Journey # :nodoc:
class Router # :nodoc: class Router # :nodoc:
...@@ -25,31 +23,58 @@ def self.normalize_path(path) ...@@ -25,31 +23,58 @@ def self.normalize_path(path)
# URI path and fragment escaping # URI path and fragment escaping
# http://tools.ietf.org/html/rfc3986 # http://tools.ietf.org/html/rfc3986
module UriEscape # :nodoc: class UriEncoder # :nodoc:
# Symbol captures can generate multiple path segments, so include /. ENCODE = "%%%02X".freeze
reserved_segment = '/' ENCODING = Encoding::US_ASCII
reserved_fragment = '/?' EMPTY = "".force_encoding(ENCODING).freeze
reserved_pchar = ':@&=+$,;' DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(ENCODING) }
safe_pchar = "#{URI::REGEXP::PATTERN::UNRESERVED}#{reserved_pchar}" ALPHA = "a-zA-Z".freeze
safe_segment = "#{safe_pchar}#{reserved_segment}" DIGIT = "0-9".freeze
safe_fragment = "#{safe_pchar}#{reserved_fragment}" UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false).freeze SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/\?]/.freeze
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
def escape_fragment(fragment)
escape(fragment, FRAGMENT)
end
def escape_path(path)
escape(path, PATH)
end
def unescape_uri(uri)
uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(uri.encoding)
end
protected
def escape(component, pattern)
component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(ENCODING)
end
def percent_encode(unsafe)
safe = EMPTY.dup
unsafe.each_byte { |b| safe << DEC2HEX[b] }
safe
end
end end
Parser = URI::Parser.new ENCODER = UriEncoder.new
def self.escape_path(path) def self.escape_path(path)
Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT) ENCODER.escape_path(path.to_s)
end end
def self.escape_fragment(fragment) def self.escape_fragment(fragment)
Parser.escape(fragment.to_s, UriEscape::UNSAFE_FRAGMENT) ENCODER.escape_fragment(fragment.to_s)
end end
def self.unescape_uri(uri) def self.unescape_uri(uri)
Parser.unescape(uri) ENCODER.unescape_uri(uri)
end end
end end
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册