XML-RPC+gzip+base64 на Ruby

Пишу здесь чтобы не забыть. Сейчас XML-RPC довольно популярен, и как показывает практика мало кто, например из интернет-магазинов, использует его в чистом виде. В основном ресурсы, где выходной XML довольно большой, любят сжимать его Zlib’ом и после чего этот бинарник шифровать base64, а-ля он вовсе и не бинарник.

XML-RPC

Для XML-RPC в Ruby замечательная библиотека с одноименным названием XMLRPC.

И для примера рассмотрим как ей пользоватся:

1
2
3
4
5
6
7
8
9
require 'xmlrpc/client'
XMLRPC::Client.class_eval do
    def parse_content_type(a)
      ['text/xml']
    end
  end
 
client = XMLRPC::Client.new2("http://site.ru/xml")
result = client.call("xmlrpc.cmd")

Перегрузка parse_content_type нужна для того, чтобы Ruby не смущался, когда заголовок у выходного документа не text/xml. Об этой штуке я прочитал на http://blog.alno.name/ru/2009/03/xmlrpc-in-ruby.

Если для XML-RPC нужна базовая авторизация, то используем

1
client = XMLRPC::Client.new3("http://site.ru/xml",'login','pass')

Однако, на моем опыте не было случаев с базовой авторизацией в XML-RPC. Часто встречалось, что логин и пароль сували как параметры в сам запрос т.е. они были как параметры в запросном XML:

1
result = client.call("xmlrpc.cmd",'login','pass')

На этом, если в ресурсе нет больше никаких примочек, в result будет уже хэш.

base64

Теперь рассмотрим вариант, если выходной XML зашифрован base64. Расшифровывать его можно так:

1
2
require 'base64'
puts Base64.decode64(result)

Однако, это не единственный вариант т.к. в библиотеке XMLRPC есть тоже класс для этого.
Приведу еще пример, как вывести зашифрованный XML во временный файл, предварительно расшифровав.

1
2
3
4
5
6
require 'base64'
require 'tempfile'
 
fl=Tempfile.new('xmlrpc')
fl.print Base64.decode64(result)
fl.close

gzip

Как правило base64 просто так не используют т.к. с точки зрения криптографии он не реализует никакой стоящей защиты данных, а для приведения бинарника к текстовом виду он как раз подойдет.

В Zlib существует 2 метода для сжатия и распаковки строки:

  • Zlib::Deflate.deflate(string, level) – сжатие
  • Zlib::Inflate.inflate(string) – распаковка

Все было бы очень легко и просто, но к сожалению, Zlib::Inflate.inflate(string) не вернет нам распакованный XML-RPC ответ, единственное, что вы увидите:

1
`inflate': incorrect header check (Zlib::DataError)

Собственно, поэтому нам и потребовалось записать расшифрованный base64 XML во временный файл, чтобы распаковать gzip другим способом:

1
2
3
4
5
6
7
8
9
require 'zlib'
include Zlib
...
fl=Tempfile.new('xmlrpc')
fl.print Base64.decode64(result)
fl.close
xmlrpc=''
Zlib::GzipReader.open(fl.path) {|gz| xmlrpc=gz.read}
fl.close(true)

fl.close(true) с флагом true удаляет временный файл.

получение хэша их XML

Вообще, существует миллион двести различных библиотек и классов для расхэширования XML, но я не буду проводить исследования и воспользуюсь, пусть и не самым быстрым, зато стандартным REXML.

1
2
3
4
require 'rexml/document'
include REXML
...
xml=Document.new xmlrpc

Все, теперь в переменной xml лежит хэш.

No Comments.

Leave a Reply

(обязательно)

(обязательно)