Избегайте двойного кодирования при экранировании фрагментов URL в Ruby - PullRequest
0 голосов
/ 06 февраля 2020

Я пытаюсь очистить некоторый автоматически сгенерированный код, где фрагменты входного URL:

  1. могут содержать пробелы, которые должны быть % -экранированы (как %20, а не *) 1006 *)
  2. может включать в себя другие недействительные символы URL, которые также должны быть % -экранированы
  3. , могут включать разделители пути, которые необходимо оставить в покое (/)
  4. может включать в себя уже экранированные компоненты, для которых необходимо , а не , чтобы экранироваться дважды

В существующем коде используется libcurl (через Typhoeus и Ethon ), который, как и командная строка curl, похоже, с радостью принимает пробелы в URL.

Существующий код основывается на строках и содержит ряд махинаций, связанных с удалением дополнительных косые черты, добавление недостающих косых черт и т. д. c. Я пытаюсь заменить это на URI.join(), но это не удается с bad URI(is not URI?) на фрагментах с пробелами.

Очевидное решение состоит в использовании (не рекомендуется) URI.escape, который экранирует пробелы, но оставляет косые черты в одиночестве:

URI.escape('http://example.org/ spaces /<"punc^tu`ation">/non-ascïï ?????/&c.')
# => "http://example.org/%20spaces%20/%3C%22punc%5Etu%60ation%22%3E/non-asc%C3%AF%C3%AF%20%F0%9D%96%88%F0%9D%96%8D%F0%9D%96%86%F0%9D%96%97%F0%9D%96%98/%EF%BC%86%EF%BD%83%EF%BC%8E" 

В основном это работает, за исключением случая (3) выше - ранее экранированные компоненты получают двойной экранирование.

s1 = URI.escape(s)
# => "http://example.org/%20spaces%20/%3C%22punc%5Etu%60ation%22%3E/non-asc%C3%AF%C3%AF%20%F0%9D%96%88%F0%9D%96%8D%F0%9D%96%86%F0%9D%96%97%F0%9D%96%98/%EF%BC%86%EF%BD%83%EF%BC%8E"
URI.escape(s)
# => "http://example.org/%2520spaces%2520/%253C%2522punc%255Etu%2560ation%2522%253E/non-asc%25C3%25AF%25C3%25AF%2520%25F0%259D%2596%2588%25F0%259D%2596%258D%25F0%259D%2596%2586%25F0%259D%2596%2597%25F0%259D%2596%2598/%25EF%25BC%2586%25EF%25BD%2583%25EF%25BC%258E" 

Рекомендуемые альтернативы URI.escape, например, CGI.escape и ERB::Util.url_encode, не подходят, поскольку они борются с косой чертой (среди других проблем):

CGI.escape(s)
# => "http%3A%2F%2Fexample.org%2F+spaces+%2F%3C%22punc%5Etu%60ation%22%3E%2Fnon-asc%C3%AF%C3%AF+%F0%9D%96%88%F0%9D%96%8D%F0%9D%96%86%F0%9D%96%97%F0%9D%96%98%2F%EF%BC%86%EF%BD%83%EF%BC%8E"
ERB::Util.url_encode(s)
# => "http%3A%2F%2Fexample.org%2F%20spaces%20%2F%3C%22punc%5Etu%60ation%22%3E%2Fnon-asc%C3%AF%C3%AF%20%F0%9D%96%88%F0%9D%96%8D%F0%9D%96%86%F0%9D%96%97%F0%9D%96%98%2F%EF%BC%86%EF%BD%83%EF%BC%8E"

Существует ли чистый, готовый способ сохранить существующие косые черты, побеги и др. c. и избегать только недопустимых символов в строке URI?

Пока что лучшее, что я смог придумать, это что-то вроде:

include URI::RFC2396_Parser::PATTERN

INVALID = Regexp.new("[^%#{RESERVED}#{UNRESERVED}]")

def escape_invalid(str)
  parser = URI::RFC2396_Parser.new
  parser.escape(str, INVALID)
end

Это кажется на работу:

s2 = escape_invalid(s)
# => "http://example.org/%20spaces%20/%3C%22punc%5Etu%60ation%22%3E/non-asc%C3%AF%C3%AF%20%F0%9D%96%88%F0%9D%96%8D%F0%9D%96%86%F0%9D%96%97%F0%9D%96%98/%EF%BC%86%EF%BD%83%EF%BC%8E"
s2 == escape_invalid(s2)
# => true 

, но я не уверен в конкатенации регулярных выражений (даже если это так URI::RFC2396_Parser работает внутренне), и я знаю, что она не обрабатывает все случаи (например, %, который не является частью действительного шестнадцатеричного побега, вероятно, должен быть экранирован). Я бы предпочел найти стандартное решение для библиотеки.

...