Why are the headers skipped in net/http? - ruby

If I do the following then the headers are skipped
require 'net/http'
require 'openssl'
require 'ap'
uri = URI("https://test:A234567#www.example.com/data")
http = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https')
request = Net::HTTP::Get.new uri
request['X-appname'] = 'testapp'
request['X-token'] = '1854fac3'
response = http.request request # Net::HTTPResponse object
ap response.body
I get the exact same error if I comment the header lines, so that is why I say they are skipped.
The error is
"<Fault xmlns=\"http://schemas.microsoft.com/ws/2005/05/envelope/none\">
<Code><Value>Receiver</Value><Subcode><Value>NotAuthorized</Value>
</Subcode></Code><Reason><Text xml:lang=\"en-US\">Wrong username or
password.</Text></Reason></Fault>"
If I in Bash do
curl -H 'X-appname: testapp' -H 'X-token: 1854fac3' https://test:A234567#www.example.com/data
then it works.
Question
Can anyone see why it doesn't wotk in by Ruby script?

Looks like the URL requires HTTP Basic Auth. The error in your case is with respect to user/password - Wrong username or password., and not related to missing headers
You should have something like this in your code:
request.basic_auth 'test', 'A234567'
and URI should be
uri = URI("https://www.example.com/data")

Related

Fetch urls prefixed with username:password#

I am using net-http-persistent gem to fetch pages. It works perfectly fine for most of the cases. But, recently I noted that it returns 401 for urls prefixed with username:password# e.g. https://username:password#somesite.com. If i try other options like excon/curl they fetch such pages without problem. I saw the logs of the requests made by Net::HTTP::Persistent and found out net::http totally discards the username:password part while connecting to the server.
Can anybody help me how to make Net::HTTP::Persistent make use of username:password# part.
----------------------EDITED--------------------
Sample code:
url = "https://user:pass#example.com/feed"
uri = URI(url)
http = Net::HTTP::Persistent.new
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
response = http.request uri
http.shutdown
response.code # yields 401 which it should not as url has username and password.
#Incase of excon, if you do
response = Excon.get(url)
response.status # yields 200 as it is making use of username:password prefix
Based on this issue, try code like:
uri = URI("https://example.com/feed")
req = Net::HTTP::Get.new(uri.request_uri)
req.basic_auth 'user', 'pass'
http = Net::HTTP::Persistent.new
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
response = http.request uri, req
http.shutdown
puts response.code

Translate Curl to Ruby (New Relic API)

I am extremly new to ruby, but I am playing around with it. Could someone help me translate this curl command to Ruby ?
I have been having a difficult time trying to adapt other ruby examples to fit my needs.
curl -X GET 'https://api.newrelic.com/v2/servers.json' \
-H 'X-Api-Key:12345689' -i \
-G -d 'filter[host]=server1'
Ruby Code:
require 'net/http'
uri = URI.parse('https://api.newrelic.com/v2/servers.json')
request = Net::HTTP::Get.new uri.request_uri
res = Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https') {|http| http.request request}
request.initialize_http_header({'X-Api-Key' => '12345689'})
request.initialize_http_header({'Accept' => 'application/json'})
request.initialize_http_header({'Content-Type' => 'application/json'})
request.set_form_data({"filter[host]" => "server1"})
response = res.request(request)
Error Message:
test.rb:16:in `<main>': undefined method `request' for #<Net::HTTPUnauthorized 401 Unauthorized readbody=true> (NoMethodError)
Should look something like this:
require 'net/http'
uri = URI('https://api.newrelic.com/v2/servers.json')
uri.query = URI.encode_www_form({ 'filter[host]' => 'server1' })
req = Net::HTTP::Get.new(uri)
req['X-Api-Key'] = '123456789'
http = Net::HTTP.new(uri.hostname, uri.port)
http.use_ssl = true
response = http.request(req)
p response.read_body
In Ruby, you can simply get the output of a command by using backticks (`), you only had to escape the special characters, like in every other string. So it would be:
`curl -X GET 'https://api.newrelic.com/v2/servers.json' -H 'X-Api-Key:12345689' -i -G -d 'filter[host]=server1'`

Cannot make HTTP Delete request with Ruby's net/http library

I've been trying to make an API call to my server to delete a user record help on a dev database. When I use Fiddler to call the URL with the DELETE operation I am able to immediately delete the user record. When I call that same URL, again with the DELETE operation, from my script below, I get this error:
{"Message":"The requested resource does not support http method 'DELETE'."}
I have changed the url in my script below. The url I am using is definitely correct. I suspect that there is a logical error in my code that I haven't caught. My script:
require 'net/http'
require 'json'
require 'pp'
require 'uri'
def deleteUserRole
# prepare request
url= "http://my.database.5002143.access" # dev
uri = URI.parse(url)
request = Net::HTTP::Delete.new(uri.path)
http = Net::HTTP.new(uri.host, uri.port)
# send the request
response = http.request(request)
puts "response: \n"
puts response.body
puts "response code: " + response.code + "\n \n"
# parse response
buffer= response.body
result = JSON.parse(buffer)
status= result["Success"]
if status == true
then puts "passed"
else puts "failed"
end
end
deleteUserRole
It turns out that I was typing in the wrong command. I needed to change this line:
request = Net::HTTP::Delete.new(uri.path)
to this line:
request = Net::HTTP::Delete.new(uri)
By typing uri.path I was excluding part of the URL from the API call. When I was debugging, I would type puts uri and that would show me the full URL, so I was certain the URL was right. The URL was right, but I was not including the full URL in my DELETE call.
if you miss the parameters to pass while requesting delete, it won't work
you can do like this
uri = URI.parse('http://localhost/test')
http = Net::HTTP.new(uri.host, uri.port)
attribute_url = '?'
attribute_url << body.map{|k,v| "#{k}=#{v}"}.join('&')
request = Net::HTTP::Delete.new(uri.request_uri+attribute_url)
response = http.request(request)
where body is a hashmap where you can define query params as a hashmap.. while sending request it can be joined in the url by the code above.
ex:body = { :resname => 'res', :bucket_name => 'bucket', :uploaded_by => 'upload' }

Header information getting lost in POST response

In a ruby POST call I am expecting some custom header named 'Authentication-Token', which is received when called from any other REST client. But when called from ruby script I am getting all headers except this required header.
Below is the code
require 'net/http'
require 'json'
require 'uri'
uri = URI.parse('http://ashish-1:9090/csm/login')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({"username" => 'test', "password" => 'test'})
request.add_field("Authentication-Token", "")
request.add_field("Authorization", "")
request.add_field("Content-Type", "application/json")
response = http.request(request)
puts response
puts response.code
puts "Headers: #{response.to_hash}" #prints all headers except Authentication-Token
puts response["session-id"] # get printed
puts response["Authentication-Token"] # blank
Any idea what is missing?
Thanks,
Ashish

How to do basic authentication over HTTPs in Ruby?

After looking a lot, I've found some solutions that seem working, but not for me...
For example, I have this script:
require 'net/http'
require "net/https"
#http=Net::HTTP.new('www.xxxxxxx.net', 443)
#http.use_ssl = true
#http.verify_mode = OpenSSL::SSL::VERIFY_NONE
#http.start() {|http|
req = Net::HTTP::Get.new('/gb/PastSetupsXLS.asp?SR=31,6')
req.basic_auth 'my_user', 'my_password'
response = http.request(req)
print response.body
}
When I run it, it gives me a page that requests for authentication, but if I write the following URL in the browser, I get into the website without problems:
https://my_user:my_password#www.xxxxxxx.net/gb/PastSetupsXLS.asp?SR=31,6
I have also tried with open-uri:
module OpenSSL
module SSL
remove_const :VERIFY_PEER
end
end
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
def download(full_url, to_here)
writeOut = open(to_here, "wb")
writeOut.write(open(full_url, :http_basic_authentication=>["my_user", "my_password"]).read)
writeOut.close
end
download('https://www.xxxxxxx.net/gb/PastSetupsXLS.asp?SR=31,6', "target_file.html")
But the result is the same, the site is asking for user authentication.
Any tips of what am I doing wrong?. Must I encode the password in Base 64?
I wrote a piece of code based on examples given in the Net::HTTP docs and tested it on my local WAMP server - it works fine. Here's what I have:
require 'net/http'
require 'openssl'
uri = URI('https://localhost/')
Net::HTTP.start(uri.host, uri.port,
:use_ssl => uri.scheme == 'https',
:verify_mode => OpenSSL::SSL::VERIFY_NONE) do |http|
request = Net::HTTP::Get.new uri.request_uri
request.basic_auth 'matt', 'secret'
response = http.request request # Net::HTTPResponse object
puts response
puts response.body
end
And my .htaccess file looks like this:
AuthName "Authorization required"
AuthUserFile c:/wamp/www/ssl/.htpasswd
AuthType basic
Require valid-user
My .htpasswd is just a one liner generated with htpasswd -c .htpasswd matt for password "secret". When I run my code I get "200 OK" and contents of index.html. If I remove the request.basic_auth line, I get 401 error.
UPDATE:
As indicated by #stereoscott in the comments, the :verify_mode value I used in the example (OpenSSL::SSL::VERIFY_NONE) is not safe for production.
All available options listed in the OpenSSL::SSL::SSLContext docs are: VERIFY_NONE, VERIFY_PEER, VERIFY_CLIENT_ONCE, VERIFY_FAIL_IF_NO_PEER_CERT, out of which (according to the OpenSSL docs) only the first two ones are used in the client mode.
So VERIFY_PEER should be used on production, which is the default btw, so you can skip it entirely.
The following is what ended up working for me:
require "uri"
require "net/http"
url = URI("https://localhost/")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = "Basic " + Base64::encode64("my_user:my_password")
response = https.request(request)
puts response.read_body
I came up with this by building a new HTTP Request in Postman, specifying the URL, choosing an Authorization Type of "Basic Auth," and inputting the credentials.
Clicking the Code icon (</>) and selecting "Ruby - Net::HTTP" will then generate a code snippet, giving you the output above.
Postman took care of encoding the credentials, but this answer helped me to dynamically set these values. You also can likely omit the "cookie" key as part of the request.

Resources