Как читать полезную нагрузку GZIP в Ruby Sinatra - PullRequest
0 голосов
/ 15 января 2019

На удаленном хосте у меня есть bash-скрипт, отправляющий простой gzip-файл YAML на мою конечную точку Ruby Sinatra:

#!/bin/bash

/bin/gzip -c /tmp/test.yaml > /tmp/test.gz

curl -i <hostname>:<port>/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @/tmp/test.gz

Мой пример приложения Ruby:

require 'sinatra'
require 'zlib'
require 'stringio'

set :port, <port>
set :bind, "<ip>"

post '/last_run_report' do
  sio = StringIO.new(request.body.to_s)
  gz = Zlib::GzipReader.new(sio).read
  test_yaml = YAML.load(gz)
end

Это дает мне следующую ошибку:

Zlib::GzipFile::Error: not in gzip format

Если мне требуется 'base64' и изменить определение конечной точки на:

post '/last_run_report' do
  raw = Base64.decode64(request.body)
  sio = StringIO.new(raw)
  gz = Zlib::GzipReader.new(sio).read
  test_yaml = YAML.load(gz)
end

Я получаю следующую ошибку:

NoMethodError: undefined method `unpack1' for #<StringIO:0x000055713e2d51b8>

Я не могу понять, неправильно ли я отправляю данные или неправильно их читаю. Пожалуйста помоги.

1 Ответ

0 голосов
/ 15 января 2019

Предполагается, что образец YAML выглядит следующим образом:

martin:
    name: Martin D'vloper
    job: Developer
    skill: Elite

Вам не нужны все лишние StringIO вещи. request.body уже является экземпляром StringIO, поэтому преобразование его в строку, а затем преобразование в StringIO не требуется:

require 'sinatra'
require 'zlib'

post '/last_run_report' do
  gz = Zlib::GzipReader.new(request.body).read
  puts YAML.load(gz)
end

Теперь сделайте ваш запрос:

curl -i localhost:4567/last_run_report -H "Content-Type: application/xml" -H "Content-Encoding: gzip" --data-binary @test.gz

И просмотреть вывод синатры:

== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from Thin
Thin web server (v1.7.2 codename Bachmanity)
Maximum connections set to 1024
Listening on localhost:4567, CTRL+C to stop
{"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}}
::1 - - [14/Jan/2019:23:24:28 -0700] "POST /last_run_report HTTP/1.1" 200 - 0.0048

Обратите внимание, что puts записал {"martin"=>{"name"=>"Martin D'vloper", "job"=>"Developer", "skill"=>"Elite"}} в консоль.

Я должен отметить, что в вашем примере следующий код не работает так, как вы ожидаете:

sio = StringIO.new(request.body.to_s)

Вы ожидаете, что сможете позвонить sio.read и получить что-то вроде этого:

\x1F\x8B\b\b\xA7z=\\\x00\x03test.yaml\x00SVp\xCCSH\xCD-\xC8\xC9\xAFLMU(JM\xCE/J\xE1\xCAM,*\xC9\xCC\xB3\xE2R\x00\x82\xBC\xC4\xDCT+\x05_\xB0\x88\x82\x8BzYN~Aj\x11X&+?\xC9J\xC1%\xB5,\x15!T\x9C\x9D\x99\x93c\xA5\xE0\x9A\x93Y\x92\n\x00\xFC\xA0\x83yZ\x00\x00\x00

Что вы на самом деле получаете, это:

#<StringIO:0x00007ffd8184bdf0>

Обратите внимание, что это буквальная строка "#<StringIO:0x00007ffd8184bdf0>", а не ссылка на объект StringIO, потому что это то, что возвращается при вызове .to_s для StringIO объекта, такого как request.body, поэтому любой последующий вызов sio.read (который подразумевается в вызове Zlib::GzipReader.new) вернет эту литеральную строку и не вернет сжатые данные, которые, как вы ожидаете, вернут, что приведет к ошибке Zlib::GzipFile::Error: not in gzip format.

Если вы хотите вернуть строковое представление StringIO, вам следует вызвать .string или .read, а не .to_s. Учитывая это, минимальное изменение, необходимое для работы вашего первого примера, должно изменить это:

sio = StringIO.new(request.body.to_s)

На это:

sio = StringIO.new(request.body.string)

Но опять же, это ненужное преобразование StringIO в строку и обратно в StringIO.

...