OpenSSL error when making http request in Ruby - ruby

I have a service that makes an http request and it looks like this:
require 'net/http'
class SenderNameService
def get_names(id)
domain = "http://www.somedomain.com"
path = "/api/orchards/users/#{id}"
url = URI.parse(domain + path)
req = Net::HTTP::Get.new(url.to_s)
token = generate_token
req.add_field("Authorization", token)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
res = http.start do |http|
http.request(req)
end
if res.code == '200'
return JSON.parse(res.body)
else
raise StandardError, "Request for sender names failed"
end
end
private
def generate_token
#Some JWT token generating logic goes here.
end
end
When I make a request, I get this error:
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: unknown protocol
from .rbenv/versions/2.3.3/lib/ruby/2.3.0/net/http.rb:933:in `connect_nonblock'
What is going on? What does that error mean?
UPDATE: The error gets fixed when I change http to https. What is going on?

http.use_ssl = true
UPDATE: The error gets fixed when I change http to https. What is going on?
A http URL by default uses port 80 and there is usually a server which speaks HTTP only. A https URL instead uses port 443 where a server speaking HTTPS resides. But, you are trying to enforce HTTPS (i.e. TLS+HTTP) on the http port where no TLS capable server listens. This means that your client starts a TLS handshake to this server but does not get a TLS response back but instead a HTTP response. Trying to interpret the response as TLS causes the error you see, i.e. unknown protocol.

That version of Ruby uses connect_nonblock in http.rb to make the connections, which requests a connection to a given socket. The socket must have an address and a port so probably it is failing because it is trying to connect to an https address though port 443 (http.use_ssl=true), so you should rather use https instead of http (requires implementation of server side SSL) or set http.use_ssl to false. Please refer to: http://apidock.com/ruby/Socket/connect_nonblock

Related

HTTP POST Data unreadable until client closes

I am trying to make a web server in ruby using socket for experimentation, and I am unable to get the payload from a post request until the client closes. Then, I can not send any data back when the client does close.
Code:
require "socket"
server = TCPServer.new($HOST, $PORT)
client = server.accept
while true
puts client.gets
#Prints all headers and request but I don't see the actual payload (key=value) until the client disconnects.
end
By using if line.chomp.length == 0 to get the end of the headers and client.read() for the data, I can get the data.

Ruby: Persistent HTTP client not receiving response on second request

I am trying to create an HTTP client that uses persistent connections. My Code works when I send my first request and get my first response. However, when I send a second request, I am unable to get a second response. I am not sure why? I got the same error when I was coding in C.
Here is the code
require 'socket'
include Socket::Constants
socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' )
socket.connect( sockaddr )
# This Works
socket.write( "GET / HTTP/1.0\r\n\r\n" )
results = socket.read
# This Works
socket.write( "GET / HTTP/1.0\r\n\r\n" )
# THIS DOESN'T WORK
results = socket.read
I do not want to use built libraries like Net::HTTP. What do I need to do to make this work?
You cannot make 2 HTTP requests on the same connection, unless you've told the server that you're expecting to do so. This is how HTTP persistent connection works. At a minimum, you have to make sure to add this to your request header:
Connection: keep-alive
Servers have differing support for persistent connections, although it's become common for servers to support basic persistent connections. Here's an SO question that asks What exactly does a “persistent connection” mean?
Start there, and you'll find what you need to know to make persistent connections work correctly. You may have to check the HTTP response headers for an indication that the server will honor your request, or you may have to check that the server didn't simply close the connection when it was finished writing the first response. On your final request through a persistent connection, you should also specify the header:
Connection: close
Also check out these resources:
IETF HTTP 1.1 specification
W3 HTTP 1.1 section 8: Persistent Connections
Safari Books Online HTTP: The Definitive Guide - Persistent Connections

Example code for SSL client authentication in ruby 1.8.7

Folks,
Can anyone provide a working example of how to do SSL client authentication in ruby 1.8.7? For the record, I am attempting to use a GoDaddy certification to perform an authenticated post request to the Windows Phone push notification service. In this case, my command-line client is using a certificate like an API key or bearer token.
I supply GoDaddy a mydomain.com.key file, and GoDaddy supplies me with a gd_bundle.crt file and a mydomain.com.crt file.
On my Net::HTTP object, I have set these parameters:
my_cert = File.read(File.join(File.dirname(__FILE__), "mydomain.com.crt")) # from godaddy
my_key = File.read(File.join(File.dirname(__FILE__), "mydomain.com.key")) # i made
http.use_ssl = true
http.cert = OpenSSL::X509::Certificate.new(my_cert)
http.key = OpenSSL::PKey::RSA.new(my_key)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
But the push notification service continues to report a 403 error saying "You do not have permission to view this directory or page using the credentials that you supplied".
Since someone out there is doing push successfully I assume the way I have configured my Net::HTTP object in ruby is incorrect. Does anyone have sample code for how I can use ruby to perform SSL client authentication?

specify proxy authentication with savonrb

I'm using savon to communicate with a soap web service.
It all works well, and I now need to get the ruby code into production where we have to go through a proxy server.
This proxy server requires authentication.
So my question is, how can I go ahead to specify proxy server authentication details with savon?
Some further info:
I've figured out that you can specify a proxy server as such:
client = Savon::Client.new do
...
http.proxy = "http://proxy.example.com"
end
Looking through the code for savon, I found that the http variable in the client block refers to the following:
def http
#http ||= HTTPI::Request.new
end
Unfortunately, going through the code for HTTPI::Request, I couldn't see a way to specify authentication for the proxy itself. Here's the code for the httpi request: https://github.com/rubiii/httpi/blob/master/lib/httpi/request.rb
Just to be clear: I'm not trying to do HTTP authentication, I'm attempting to perform proxy authentication.
When specifying the proxy server, I get the following error, because I can't find a way to specify the proxy authentication credentials:
407 "Proxy Authentication Required"
Thanks in advance for any help.
Try this:
http.proxy = "http://username:password#host:port"
Managed to resolve it with the following code:
#proxy_path = '<insert your proxy url here>'
#wsdl_path = '<insert your wsdl url here>'
#client = Savon.client do |variable|
variable.proxy #proxy_path
variable.wsdl #wsdl_path
end
#client.operations #to list operations

How do I send HTTP requests over a TCPSocket?

I've written a class in Ruby that acts as an HTTP client. The code is minimal but the reason I'm not using 'net/http' is because this method allowes me to have more control over the requests being made and documentation for the HTTP is not helpful at all.
Anyway, the problem is the socket will only work for one request and response. Sending a second or subsequent request gives me an empty response.
For example:
Open connection to google
GET "/"
Response is the google.ca html
GET "/"
Response is empty
I tried closing and opening the connection between the requests but that only slowed it down and didn't fix the problem. I still got empty responses.
So what is the problem here?
Is there a method that lets me check to see if the TCPSocket object has an open connection so I don't accidentally open a new one?
Try:
require "socket"
host = "google.com"
port = 80
socket = TCPSocket.new host,port
request = "GET / HTTP/1.0\r\nHost:#{host}\r\n\r\n"
socket.print request
response = socket.read
This will return Google's main page. If you want to send request after request then change it to "HTTP/1.1" and read a response, then send the next request.

Resources