Net::HTTP.get fails with HTTPS with Headers - ruby

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

Related

Copied and pasted Ruby code from Hubspot API but I get an HTTPUnsupportedMediaType415

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.

Firebase Cloud Function no longer receiving calls from ruby's Net:HTTP

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.

Httpi timeout on PUT but POST works

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)

Studio 3 error message re W3C validation

Just a quick question from a newbie. Has anyone else using Aptana Studio 3 been getting any error messages when using W3C validation?
Status: 403 Forbidden Vary: Referer Content-type: text/html
Markup Validation Service
Sorry! This document can not be checked.
No User-Agent header found!
A quick Google suggests that other editor/IDE users are experiencing something similar, e.g. HTML-Kit. It looks as if the W3C validation service is expecting a user-agent string as would be provided directly by a browser but presumably isn't by an editor/IDE?
I know that there are ways around the issue by using a different validation service or checking the code via a browser. Just thought I would flag it up.
I've submitted a fix to the Aptana Studio project for this.
this fix involves adding a http user-agent to the post that is sent to w3c.
w3c_validation.rb
Replace the text in the w3c_validation.rb file with the text below.
example path:
C:\Users\user\AppData\Local\Aptana Studio 3\configuration\org.eclipse.osgi\bundles\101\1.cp\bundles\html.ruble\commands\w3c_validation.rb
require 'ruble'
command t(:validate_syntax) do |cmd|
cmd.key_binding = 'CONTROL+M2+V'
cmd.scope = 'text.html'
cmd.output = :show_as_html
cmd.input = :document
cmd.invoke do |context|
$KCODE = 'U'
page = $stdin.read
page.gsub!(/<\?(php|=).*?\?>|<%.*?%>/m, '')
w3c_url = 'http://validator.w3.org/check'
require 'net/http'
require 'uri'
#fix for w3c blocking http requests without a user-agent
#changed the way the http post is sent to w3c so that it includes a user-agent
uri = URI(w3c_url)
req = Net::HTTP::Post.new(uri.path)
req.set_form_data({'ss' => "1", 'fragment' => page})
req['User-Agent'] = 'Aptana'
response = Net::HTTP.start(uri.host, uri.port) do |http|
http.request(req)
end
status = response['x-w3c-validator-status']
content = response.body
content = content.gsub(/<\/title>/, '\&<base href="http://validator.w3.org/">')
# content.gsub!(/Line (\d+),? Column (\d+)/i) do
# # FIXME These links won't work for us!
# "<a href='txmt://open?line=#\$1&column=#{\$2.to_i + 1}'>#\$&</a>"
# end
content
end
end
Your browser in your IDE is non-compliant with W3C HTTP standards
Additionally, you can't check pages that haven't been published to the web with the web tool. You need to set up a small test server to use it.
First, use a different browser. All browsers should send a user-agent string, no websites should depend on anything more than the existence of that string, or to black-list known bad browsers.
You really should test your code in a compliant browser such as Chrome or Firefox with a compliance testing developer tool set, not from your IDE.
If you must, you can instead download the client side version of the tools from the W3C website to check your code. It is a command line tool available from all platforms. There are extensions for Firefox, Chrome and derivatives available that use the library version of these tools. (Again, you can't use your IDE browser.)

Writing unit tests in Ruby for a REST API

I've written a basic REST API using sinatra.
Does anyone know the best way to write tests for it? I would like to do so using Ruby.
I've done my initial testing using curl. But I'd like to do something more robust. This is my first API - is there anything specific I should be testing?
The best way is a matter of opinion :) Personally, I like simple and clean. With tools like minitest, Watir and rest-client, you can put together a very simple test of both your REST interface as well as testing your web service through actual browsers (all major browsers are supported).
#!/usr/bin/ruby
#
# Requires that you have installed the following gem packages:
# json, minitest, watir, watir-webdrive, rest-client
# To use Chrome, you need to install chromedriver on your path
require 'rubygems'
require 'rest-client'
require 'json'
require 'pp'
require 'minitest/autorun'
require 'watir'
require 'watir-webdriver'
class TestReportSystem < MiniTest::Unit::TestCase
def setup
#browser = Watir::Browser.new :chrome # Defaults to firefox. Can do Safari and IE too.
# Log in here.....
end
def teardown
#browser.close
end
def test_report_lists # For minitest, the method names need to start with test
response = RestClient.get 'http://localhost:8080/reporter/reports/getReportList'
assert_equal response.code,200
parsed = JSON.parse response.to_str
assert_equal parsed.length, 3 # There are 3 reports available on the test server
end
def test_on_browser
#browser.goto 'http://localhost:8080/reporter/exampleReport/simple/genReport?month=Aug&year=2012'
assert(#browser.text.include?('Report for Aug 2012'))
end
end
Run the test cases by simply executing the script. There are many other testing systems and REST clients for Ruby which can be put to work in a similar way.
You might have a look at this approach http://anthonyeden.com/2013/07/10/testing-rest-apis-with-cucumber-and-rack.html
although many might say that using Cucumber is really more application or Acceptance testing and not unit testing, it does contain an approach to creating the HTTP headers and forming the http request, which I'm guessing might be where you are stuck?
Personally I don't have a problem with that since if you are truely going to unit test the API, you'd likely have to mock any units of code the api might be talking with (e.g. however you are persisting the data)
Seeing as I'm a QA guy not a dev, I'd be perfectly happy with using cucumber and testing it at that level, but I also greatly appreciate it when devs unit test, so while you might use rSpec instead of Cuke, perhaps the tip towards 'rack test' will be useful to what you are trying to accomplish.
You can try using airborne which is a framework written for just this purpose:
https://github.com/brooklynDev/airborne
You can test against either a live API, or against a Sinatra, Grape, Rails application.
I would use fakeweb gem to do unit testing with web services.
I would suggest client-api gem - it has loads of useful features specific to api automation which is easy to use and to maintain scripts.
https://github.com/prashanth-sams/client-api
Interestingly, this gem binds an api automation framework within itself. So, you don't even need a framework setup.
Key Features of client-api library:
Custom Header, URL, and Timeout support
URL query string customization
Datatype and key-pair value validation
Single key-pair response validation
Multi key-pair response validation
JSON response schema validation
JSON response content validation
JSON response size validation
JSON response is empty? validation
JSON response has specific key? validation
JSON response array-list sorting validation (descending, ascending)
Response headers validation
JSON template as body and schema
Support to store JSON responses of each tests for the current run
Logs support for debug
Custom logs remover
Auto-handle SSL for http(s) schemes
Example specs: https://github.com/prashanth-sams/client-api/tree/master/spec/client
Add this config snippet in the spec_helper.rb file:
ClientApi.configure do |config|
config.base_url = 'https://reqres.in'
config.headers = {'Content-Type' => 'application/json', 'Accept' => 'application/json'}
config.basic_auth = {'Username' => 'ahamilton#apigee.com', 'Password' => 'myp#ssw0rd'}
config.json_output = {'Dirname' => './output', 'Filename' => 'test'}
config.time_out = 10 # in secs
config.logger = {'Dirname' => './logs', 'Filename' => 'test', 'StoreFilesCount' => 2}
end
RSpec test scenarios look like,
api = ClientApi::Api.new
it "GET request" do
api.get('/api/users')
expect(api.status).to eq(200)
expect(api.message).to eq('OK')
end
it "POST request" do
api.post('/api/users', {"name": "prashanth sams"})
expect(api.status).to eq(201)
end
Note: This is an active project handling issues and new features based on user requirements

Resources