httpclient icon indicating copy to clipboard operation
httpclient copied to clipboard

PATCH request fails but PUT works

Open jblaine opened this issue 9 years ago • 9 comments

I'm stumped. What am I doing wrong?

require 'jsonclient'

hostdb_base_url = 'http://ourserver.our.org:8000'
hostdb_api_url = "#{hostdb_base_url}/api/v1"

fqdn = 'rcf-test-ubuntu.our.org'

now = Time.now
datestr = "#{now.year}-#{now.month}-#{now.day} #{now.hour}:#{now.min}"

client = JSONClient.new
response = client.get("#{hostdb_api_url}/device/?name=#{fqdn}&format=json")
devices = response.content['objects']
device_uri = devices.first['resource_uri']
puts "Trying to PATCH #{hostdb_base_url}#{device_uri}"
response = client.patch("#{hostdb_base_url}#{device_uri}", {:last_checkin => datestr})

Results in the following:

Trying to PATCH http://ourserver.our.org:8000/api/v1/device/767/
httpclient-2.7.1/lib/httpclient/session.rb:795:in `block in parse_header': HTTPClient::KeepAliveDisconnected:  (HTTPClient::KeepAliveDisconnected)
        from /opt/chef/embedded/lib/ruby/2.1.0/timeout.rb:91:in `block in timeout'
        from /opt/chef/embedded/lib/ruby/2.1.0/timeout.rb:101:in `call'
        from /opt/chef/embedded/lib/ruby/2.1.0/timeout.rb:101:in `timeout'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient/session.rb:788:in `parse_header'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient/session.rb:771:in `read_header'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient/session.rb:547:in `get_header'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1294:in `do_get_header'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1241:in `do_get_block'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1021:in `block in do_request'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1134:in `rescue in protect_keep_alive_disconnected'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1128:in `protect_keep_alive_disconnected'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:1016:in `do_request'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:858:in `request'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/jsonclient.rb:29:in `request'
        from /opt/chef/embedded/lib/ruby/gems/2.1.0/gems/httpclient-2.7.1/lib/httpclient.rb:749:in `patch'
        from ./_update-basic.rb:19:in `<main>'
root@rcf-test-ubuntu:/tmp#

jblaine avatar Mar 29 '16 21:03 jblaine

This is httpclient 2.7.1 from rubygems.

jblaine avatar Mar 29 '16 21:03 jblaine

Turns out that switching from PATCH to PUT works.

However, testing with rest-client's PATCH (https://rubygems.org/gems/rest-client/) works fine.

jblaine avatar Mar 30 '16 14:03 jblaine

HTTP put is supposed to be idempotent, patch is not. Which means if a put failed, it can be transparently re-tried, but a patch can not be.

I'm guessing HTTPClient has no way to notice a dropped persistent connection until it tries to make a request and get an error. It can transparently re-try idempotent request methods, but not non-idempotent request methods.

You can probably tell HTTPClient not to use persistent connections?

jrochkind avatar Mar 30 '16 15:03 jrochkind

Turns out that switching from PATCH to PUT works.

However, testing with rest-client's PATCH (https://rubygems.org/gems/rest-client/) works fine.

jblaine avatar Apr 06 '16 17:04 jblaine

Right, you already said that? I don't know anything about rest-client, it may not be using persistent HTTP keep-alive connections at all in which case the issue isn't present (which is why I suggested trying to tell HTTPClient to not use persistent connections for your case), or it may be violating the HTTP spec. Or something else.

jrochkind avatar Apr 06 '16 18:04 jrochkind

Oops. Sorry. I found a browser tab open with my comment not apparently submitted yet, so I submitted it.

We've just settled on using PUT as it works for our specific use case.

If you (whomever) feel this isn't a bug in httpclient, I guess close?

jblaine avatar Apr 08 '16 13:04 jblaine

I'm not actually a committer, see what @nahi says!

jrochkind avatar Apr 08 '16 14:04 jrochkind

@jblaine Sorry for late response. I guess something I'm not aware of is happening at parsing 'PATCH' response. Could you try to add

client.debug_dev = STDERR

and try PATCH request? I want to see wiredump.

@jrochkind yeah, PUT should be idempotent but at this point HTTPClient does not assume that and does no retry based on method type.

nahi avatar Jun 20 '16 11:06 nahi

Hmm. This works now with httpclient-2.8.3

[@gazoo:~] $ ruby test.rb
Trying to PATCH http://our-server.example.org:8000/api/v1/device/910/
[@gazoo:~] $

jblaine avatar Jun 27 '17 19:06 jblaine