2

I am posting to a remote server that I do not have control over. The params that are being passed are definitely a factor in the problem but I am having a hard time figuring out why it is failing. It does not look like there is a big enough change to create an error. I have never handled zlib compression directly. Is there a way to get better error output? Example:

This passes:

 {"email" => "a@bc.com"}

This fails:

 {"email" => "a@bd.com"}

Ruby 2.2 / Rails 4

The error:

 #<Zlib::Inflate:0x0000000618ea38 @dictionaries={}>
 #<Zlib::DataError: invalid block type>
 "invalid block type"
[ 0] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:377:in `inflate'",
[ 1] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:377:in `block in inflate_adapter'",
[ 2] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/protocol.rb:411:in `call_block'",
[ 3] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/protocol.rb:402:in `<<'",
[ 4] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/protocol.rb:106:in `read'",
[ 5] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:399:in `read'",
[ 6] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:317:in `read_chunked'",
[ 7] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:281:in `block in read_body_0'",
[ 8] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:260:in `inflater'",
[ 9] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:279:in `read_body_0'",
[10] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:201:in `read_body'",
[11] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:226:in `body'",
[12] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http/response.rb:163:in `reading_body'",
[13] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:1411:in `catch'",
[14] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:1411:in `transport_request'",
[15] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:1384:in `request'",
[16] "/home/pete/.rvm/gems/ruby-2.2.2@umbie/gems/rest-client-1.6.9/lib/restclient/net_http_ext.rb:51:in `request'",
[17] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:1377:in `block in request'",
[18] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:853:in `start'",
[19] "/home/pete/.rvm/rubies/ruby-2.2.2/lib/ruby/2.2.0/net/http.rb:1375:in `request'",
[20] "/home/pete/.rvm/gems/ruby-2.2.2@umbie/gems/rest-client-1.6.9/lib/restclient/net_http_ext.rb:51:in `request'"

Method that throws error in response.rb

 def inflate_adapter(dest)
  if dest.respond_to?(:set_encoding)
    dest.set_encoding(Encoding::ASCII_8BIT)
  elsif dest.respond_to?(:force_encoding)
    dest.force_encoding(Encoding::ASCII_8BIT)
  end
  block = proc do |compressed_chunk|
    @inflate.inflate(compressed_chunk) do |chunk| # This is line 377
      dest << chunk
    end
  end

  Net::ReadAdapter.new(block)
end

3rd party library:

  require 'net/http'
  require 'uri'
  require 'cgi'
  require 'rubygems'
  require 'json'
  module ConnectionHelper
    class Connection
      attr_accessor :practiceid
      attr_reader :token

      def initialize(version, key, secret, practiceid=nil)
        uri = URI.parse('https://api.remote_url.com/')
        @connection = Net::HTTP.new(uri.host, uri.port)
        @connection.use_ssl = true
        # Monkey patch to make Net::HTTP do proper SSL verification.
        # Background reading:
        # http://stackoverflow.com/a/9238221
        # http://blog.spiderlabs.com/2013/06/a-friday-afternoon-troubleshooting-ruby-openssl-its-a-trap.html
        def @connection.proper_ssl_context!
          ssl_context = OpenSSL::SSL::SSLContext.new
          ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
          cert_store = OpenSSL::X509::Store.new
          cert_store.set_default_paths
          ssl_context.cert_store = cert_store
          @ssl_context = ssl_context
        end
        @connection.proper_ssl_context!
        # End monkey patch
        @version = version
        @key = key
        @secret = secret
        @practiceid = practiceid

        authenticate
      end

      def authenticate            # :nodoc:
        auth_paths = {
          'v1' => 'oauth',
          'preview1' => 'oauthpreview',
          'openpreview1' => 'oauthopenpreview',
        }
        request = Net::HTTP::Post.new("/#{auth_paths[@version]}/token")
        request.basic_auth(@key, @secret)
        request.set_form_data({'grant_type' => 'client_credentials'})
        response = @connection.request(request)
        authorization = JSON.parse(response.body)
        @token = authorization['access_token']
      end

      def path_join(*args)        # :nodoc:
        head = '^/+'
        tail = '/+$'
        # add a slash to each slash-trimmed string, grab the non-empty ones, and join them up
        return args.map { |arg| '/' + arg.to_s.gsub(/#{head}|#{tail}/, '') }.select { |x| !x.empty? }.join('')
      end

      def call(request, body, headers, secondcall=false)
        begin
          request.set_form_data(body)
          headers.each {
            |k, v|
            request[k] = v
          }
          request['authorization'] = "Bearer #{@token}"
          response = @connection.request(request)

          if response.code == '401' && !secondcall
            authenticate
            return call(request, body, headers, secondcall=true)
          end
          return JSON.parse(response.body)
        rescue Exception => e
          puts e.message
        end
      end

      def POST(path, parameters=nil, headers=nil)
        url = path
        parameters ||= {}
        headers ||= {}
        request = Net::HTTP::Post.new(path_join(@version, @practiceid, url))
        return call(request, parameters, headers)
      end

    private :authenticate, :path_join, :call
    end
  end

Update

I am able to get a valid response from the web view API tester when it fails. I have posted a good and bad response below. It would seem that there is a validation error on the remote servers end and for some reason their response can not be parsed by zlib.

Bad response:

 #Header
 Content-Type: application/json
 Date: Wed, 22 Jun 2016 19:39:59 GMT
 Nncoection: close
 Pragma: No-cache
 Server: Apache
 Vary: Accept-Encoding
 X-Mashery-Message-Id: 524sd54sd-21sd-s5d4-89ds-54sd54sd4s5d4
 X-Mashery-Responder: some_site.com
 Transfer-Encoding: chunked
 Connection: keep-alive

 #Body
 {
  "fields": ["email", "mobilephone", "workphone", "zip"],
  "error": "Data for one or more of the fields listed above are required to successfully create or find a patient record. Note: invalid phone numbers are ignored."
 }

Good Response:

 #Header
 Cache-Control: no-cache, no-store
 Content-Type: application/json
 Date: Wed, 22 Jun 2016 19:41:19 GMT
 Expires: Mon, 06 Jan 1975 16:00:00 GMT
 Nncoection: close
 Pragma: No-cache
 Server: Apache
 Vary: Accept-Encoding
 X-Mashery-Message-Id: s5ad4as5d-5s4d-5s4d-3545-s5d4s5d453s4d
 X-Mashery-Responder: some_site.com
 Content-Length: 22
 Connection: keep-alive

 #Body
 [{
   "personid": "4131"
 }]

I do not know if it matters but there is no "accept-encoding" and it defaults to: “gzip;q=1.0,deflate;q=0.6,identity;q=0.3”.

Peter Black
  • 1,142
  • 1
  • 11
  • 29
  • That means that you are feeding invalid deflate data to inflate. A better error message won't help you figure out whatever the problem is upstream of inflate. – Mark Adler Jun 21 '16 at 16:21
  • 1
    Forgive me if I do not completely understand, but isn't the data coming from the remote servers response? Does that mean the response is not in the right format? Can you point me in the direction of further debugging? – Peter Black Jun 21 '16 at 20:21

1 Answers1

4

The net/http did not know how to handle the response. The solution was to set the request 'accept-encoding' to 'identity'.

    request.set_form_data(body)
    headers.each {
      |k, v|
      request[k] = v
    }

    request["User-Agent"] = "Mozilla/5.0"
    request["accept-encoding"] = "identity"
    request['authorization'] = "Bearer #{@token}"
Peter Black
  • 1,142
  • 1
  • 11
  • 29