HTTPS POST with Args & Authentication in Ruby - ruby

I am trying to write some Ruby code to create Github repos using the API, I came up with this (based on successfully reading the repos available):
uri = URI("https://api.github.com/user/repos")
args = {:name => 'apitest2'}
uri.query = URI.encode_www_form(args)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri)
request.basic_auth('kemra102', 'myapitoken')
response = http.request(request)
Sadly this errors with:
#<Net::HTTPBadRequest 400 Bad Request readbody=true>
at the irb prompt.
Any pointers? I'm still new to Ruby and development in general so reading the documentation for all these functions has probably been more confusing than helpful.

GitHub requires Content-Type: application/json; charset=utf8 header to be sent and payload to be sent in JSON instead of application/x-www-form-urlencoded format.
See here how to use Net::HTTP to POST JSON 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

Multipart POST Ruby HTTPS

I am trying to do a multipart post with parameters in ruby securely using https. All the examples I have seen are only http without parameters in addition to the file. However, I can't seem to modify them to get them to work with https and additional parameters (or find documentation showing a good example). How can I do a multipart POST using HTTPS in ruby with parameters? I have tried modify the code from Nick Sieger as shown below but to no avail. Where do I add parameters that I need to pass in in JSON format in addition to the file?
# push file to rest service
url = URI.parse('https://some.url.test/rs/test')
File.open(tm.created_file_name) do |txt|
req = Net::HTTP::Post::Multipart.new url.path,
'file' => UploadIO.new(txt, 'text/plain', tm.created_file_name)
n = Net::HTTP.new(url.host, url.port)
n.use_ssl = true
p req.body_stream
res = n.start do |http|
response = http.request(req)
p response.body
end
end
I figured out to do a multipart form post using https and parameters. Here is the code:
require 'rest-client'
url = 'https://some.url/rs/FileUploadForm'
#res = RestClient.post url, {:multipart=>true,:tieBreakerOptions=>1,
:myFileName=>'file.txt',
:myFile=>File.new('data/file.txt','r')}
response = JSON.parse(#res)

Net::HTTP not sending request

I have the following method in our Ruby 1.8.7 project:
def self.ping_server
request_data = get_request_data
uri = 'another_server.our_company.com'
https = Net::HTTP.new(uri)
https.use_ssl = true
path = "/our_service"
data = request_data.to_json
response = https.post(path, data, {'Content-Type' => 'application/json'})
return response
end
Whenever I run this method, I get the following time out error:
Completed 500 Internal Server Error in 128936ms
Errno::ETIMEDOUT (Connection timed out - connect(2)):
lib/my_class.rb:51:in `ping_our_server'
I checked with a coworker who has access to the logs for another_server.our_company.com. My request isn't arriving at the other server.
What should I do to get my request to work?
EDIT: On further inspection, this is what I think is happening (but I'm not completely sure): Our other server will only accept HTTPS requests, but it looks like my request is being sent over HTTP for some reason. Is there something I need to add to make sure my request is sent over HTTPS?
According to this website, this is how you send an HTTPS request:
require "net/https"
require "uri"
uri = URI.parse("https://secure.com/")
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)
response.body
response.status
response["header-here"] # All headers are lowercase
According to this website (which is linked from my first link), you should also do this to close up a vulnerability in the net/https library:
To get going, you need a local CA certificates bundle, the official
curl site maintains an up to date cacert.pem / ca-bundle.crt file
containing all of the major certificates if you need one.
Next, after a gem install always_verify_ssl_certificates, you can be
up and running with a test as simply as:
require 'always_verify_ssl_certificates'
AlwaysVerifySSLCertificates.ca_file = "/path/path/path/cacert.pem"
http= Net::HTTP.new('https://some.ssl.site', 443)
http.use_ssl = true
req = Net::HTTP::Get.new('/')
response = http.request(req)
If the site
has a bad certificate an error will be raised at this point. If not, a
legitimate HTTP response object will be returned.

Ruby equivalent for setting HTTP GET headers

In C# it was fairly simple and didn't take more than a couple minutes to google:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(#"http://www.example.com?q=someValue");
request.Headers.Add("Authorization: OAuth realm=\"example.com\" oauth_consumer_key=\"BCqrstoO\" ... so on and so forth");
string resultString = "";
using (StreamReader read = new StreamReader(request.GetResponse().GetResponseStream(), true))
{
resultString = read.ReadToEnd();
}
Trying to do it in Ruby hasn't quite been as straight forward (or is just something stupid that I'm missing).
I have been looking and the closest things I've come to finding my answer are How to make an HTTP GET with modified headers? and Send Custom Headers in Ruby.
So my problem, I suppose, boils down to
How do I set the headers as just a just a straight forward string?
Why do these two examples show headers formatted the way they are?
Is what I'm asking for even good convention and if not, how do I format what I'm trying to do in the convention these Ruby methods are asking for?
So far I tried the two examples and here's my most recent non-working attempt:
headers = "Authorization: OAuth realm=\"example.com\" oauth_consumer_key=\"BCqrstoO\" ... so on and so forth"
uri = URI("www.example.com")
http = Net::HTTP.new(uri.host, uri.port)
http.get(uri.path, headers) do |chunk|
puts chunk
end
Use open-uri. Example:
require 'open-uri'
open("http://www.ruby-lang.org/en/",
"User-Agent" => "Ruby/#{RUBY_VERSION}",
"From" => "foo#bar.invalid",
"Referer" => "http://www.ruby-lang.org/") {|f|
# ...
}
Just in case you check this at this point on time, the Net:HTTPRequest object allows you to add headers easily.
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Get.new uri
request['my-header'] = '1'
http.request request do |response|
puts response
end
end

Accessing Headers for Net::HTTP::Post in ruby

I have the following bit of code:
uri = URI.parse("https://rs.xxx-travel.com/wbsapi/RequestListenerServlet")
https = Net::HTTP.new(uri.host,uri.port)
https.use_ssl = true
req = Net::HTTP::Post.new(uri.path)
req.body = searchxml
req["Accept-Encoding"] ='gzip'
res = https.request(req)
This normally works fine but the server at the other side is complaining about something in my XML and the techies there need the xml message AND the headers that are being sent.
I've got the xml message, but I can't work out how to get at the Headers that are being sent with the above.
To access headers use the each_header method:
# Header being sent (the request object):
req.each_header do |header_name, header_value|
puts "#{header_name} : #{header_value}"
end
# Works with the response object as well:
res.each_header do |header_name, header_value|
puts "#{header_name} : #{header_value}"
end
you can add:
https.set_debug_output $stderr
before the request and you will see in console the real http request sent to the server.
very useful to debug this kind of scenarios.
Take a look at the docs for Net::HTTP's post method. It takes the path of the uri value, the data (XML) you want to post, then the headers you want to set. It returns the response and the body as a two-element array.
I can't test this because you've obscured the host, and odds are good it takes a registered account, but the code looks correct from what I remember when using Net::HTTP.
require 'net/http'
require 'uri'
uri = URI.parse("https://rs.xxx-travel.com/wbsapi/RequestListenerServlet")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true
req, body = https.post(uri.path, '<xml><blah></blah></xml>', {"Accept-Encoding" => 'gzip'})
puts "#{body.size} bytes received."
req.each{ |h,v| puts "#{h}: #{v}" }
Look at Typhoeus as an alternate, and, in my opinion, easier to use gem, especially the "Making Quick Requests" section.

Resources