Question
Why, suddenly, do all calls to Firebase Cloud Function webhooks timeout when made via ruby's standard HTTP library (Net::HTTP)?
Background
This works just fine:
require 'net/http'
require 'json'
uri = URI("https://postb.in/1570228026855-4628713761921?hello=world")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Post.new(uri)
req['Content-Type'] = 'application/json'
req.body = {a: 1}.to_json
http.request(req)
end
However the same script does not work with a Cloud Function URL in place of the postb.in one.
Making the same POST request to the Cloud Function URL via cURL works. It's only when made via ruby Net:HTTP library where it's timing out:
/usr/lib/ruby/2.5.0/net/http.rb:937:in `initialize': execution expired (Net::OpenTimeout)
This function has been called many times per second over the past several months, from a Ruby Net:HTTP Post without issue. And it suddenly stopped working last night. I've tested on multiple servers with ruby versions 2.3.8 and also 2.5.
The Cloud Function code is:
export const testHook = functions.https.onRequest((request, response) => {
console.log(request)
response.status(200).send('works')
})
The answer ended up being to add require 'resolv-replace' to the ruby script making use of Net::HTTP to make the HTTP POST. Found that thanks to: Ruby Net::OpenTimeout: execution expired
Why Net:HTTP is able to resolve postbin URLs but not Firebase ones, and why it was able to resolve Firebase ones successfully for many months until suddenly not, I can't explain.
Related
Ruby v2 introduced Net:HTTP.get(uri) which allowed for handling HTTPS URI objects seamlessly.
For example, you can call Net:HTTP.get(URI('https://google.com')) without any special incantations.
However, the minute I supply any headers as a second argument. I get a type error.
require 'net/http'
Net::HTTP.get(URI('https://google.com'), { 'Accept': 'text/html' })
# => Caused by TypeError: no implicit conversion of URI::HTTPS into String
Is there any workaround that doesn't require using the older methods?
I just had this same issue. My problem was that my ruby version (2.7) does not support passing headers to Net::HTTP.get.
https://ruby-doc.org/stdlib-2.7.0/libdoc/net/http/rdoc/Net/HTTP.html#get-method
The headers support has been included in ruby 3
https://ruby-doc.org/stdlib-3.0.0/libdoc/net/http/rdoc/Net/HTTP.html#get-method
I am simply trying to do an HTTP PUT request using a Ruby script, and I am literally copying and pasting 100% of the same thing from Hubspot's example. It's working in Hubspot's example, but not mine.
For example, here's the 99% full code from HubSpot API (with my API key redacted):
# https://rubygems.org/gems/hubspot-api-client
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://api.hubapi.com/crm/v3/objects/deals/4104381XXXX/associations/company/530997XXXX/deal_to_company?hapikey=XXXX")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Put.new(url)
request["accept"] = 'application/json'
response = http.request(request)
puts response.read_body
When initiated by hubspot, the response is an HTTP 201, but in my Ruby script it's giving me the following error:
=> #<Net::HTTPUnsupportedMediaType 415 Unsupported Media Type readbody=true>
I have tried directly copying and pasting the exact same thing, but no luck. I would copy what I'm using, but it's 100% the same code as above except for the redacted API, deal, and company IDs. I have copied and pasted HubSpot's example directly into my rails console, but I get an unsupported media type error.
I have also tried adding a body to the request, such as request.body = "hello" and nothing.
Any suggestion would be greatly appreciated.
After analyzing a working cURL request and the ruby script via BurpSuite, I determined that the following HTTP header in the request was the culprit:
Content-Type: application/x-www-form-urlencoded
For whatever reason, the Ruby code in the original post uses this content-type by default, even though the user doesn't specify it. Makes no sense.
I am trying to get PUT working with httpi. I am using curb (curl) as my adapter with gss negotiation (I need to use a Kerberos token). I am able to GET the article_to_update and I'm able to POST new articles using almost the exact same code (remove article_to_update from the URL and change put to post). With the code below I'm getting 408 errors: "Server timeout waiting for the HTTP request from the client." I've also tried an empty body and got the same results.
Any ideas on how to get this working or to debug further?
Alternate (non-rails) solutions for kerberos-authenticated GET/PUT/POST implementations are welcomed as well. This is for a REST API but I didn't see if/how the rest-client gem supported kerberos.
require 'curb'
require 'httpi'
require 'json'
HTTPI.adapter = :curb
url = URI.escape('https://myserver.com/rest/article/article_to_update')
request = HTTPI::Request.new(url: url, open_timeout: 30)
request.headers['Content-Type'] = 'application/json'
request.headers['Accept'] = 'application/json'
request.auth.gssnegotiate
request.body = JSON.dump(
name: 'discovery',
owner: 'greg'
)
response = HTTPI.put(request)
I'm trying to do a PUT call on a Google Groups API using Ruby and the OAuth2 GEM. I've managed to authenticate OK, and the GET call works properly, but I can't seem to get the call to use the PUT method. I thought the following would work, since OAuth2 uses Faraday, but I just keep getting the 400 message back, with an indication that something's "required":
data = access_token.put('https://www.googleapis.com/groups/v1/groups/{email address}?alt=json').parsed do |request|
request.params['email'] = "{email address}"
end
Has anyone got a working example of passing parameters to a PUT request?
OK. Looks like the ".parsed" was interfering with the call here's what works, with some additions to the request object:
response = access_token.put('https://www.googleapis.com/groups/v1/groups/{email address}') do |request|
request.headers['Content-Type'] = 'application/json'
request.body='{"email": "{email address}"}'
end
# check this
puts response.status
# works if it's 200
I have obtained a valid api key from Google Places API. I need to use this on the backend, so I get a server-side key. However, the call does not work using curl nor the Rails console.
It DOES, however, work thru the browser. That said, I have triple checked that I am using the server-side key that I generated. I'm also only using the sample URL that is in the Google places documentation, so all params should be correct. Here is my curl:
curl -v https://maps.googleapis.com/maps/api/place/search/xml?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=my_key
Also, in (Ruby) Rails console:
Net::HTTP.get_response(URI.parse("https://maps.googleapis.com/maps/api/place/search/xml?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=my_key"))
Any ideas? It seems like multiple people have had issues, but there is nothing specific out there for server keys not working.
Thanks!
With CURL, be sure to put quotes around the URL. Otherwise, if you're working in Linux, the URL will be truncated after the first ampersand, which will cause a REQUEST_DENIED response.
For HTTPS with Ruby, the following should work (ref http://www.rubyinside.com/nethttp-cheat-sheet-2940.html):
require "net/https"
require "uri"
uri = URI.parse("https://maps.googleapis.com/maps/api/place/search/xml?location=-33.8670522,151.1957362&radius=500&types=food&name=harbour&sensor=false&key=...")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(uri.request_uri)
response = http.request(request)
print response.body