HTTP POST Data unreadable until client closes - ruby

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.

Related

Ruby Net:Http get request gives different response than with Browser

I am trying to fetch from API server using Net::HTTP.
puts "#{uri}".green
response = Net::HTTP.new('glassdoor.com').start { |http|
# always proxy via your.proxy.addr:8080
response = http.get(uri, {'Accept' => 'application/json'})
puts "Res val: #{response.body}".blue
}
I got the uri from the console and pasted in the browser, and I received the JSON response.
But using the Ruby Net::HTTP get I receive some security message:
Why the difference? The browser and the Ruby script are behind the same public IP.
You were detected as a crawler (correctly, by the way). Note that those requests (from browser and the script) are not just the same. The browser sends some headers, such as accepted language, user agent etc. You can peek into it using web inspector tool in the browser. On the other side, in your script you only set Accept header (and to JSON, suspicious on its own, as browser would never do that). And you do not send any user agent. It's easy to see that this is an automates request, not natural traffic from the browser.

Get Instagram Access Token using Restful Ruby

I am trying to use the Instagram API to create a rails background worker to query hashtags. I don't need to log in any other user but myself furthermore I don't want to have to use any browsers, just RESTful calls.
I'm trying to automate getting my access token in a Ruby script using the gem "rest-client" (https://github.com/rest-client/rest-client)
I can successfully navigate to the following url in a browser and get the access token from the response url
I have used both this URL:
https://www.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=xxx&response_type=token
BUT When I use the RESTful gem response = RestClient.get(url) the
response.headers['location'] is nil
I have also tried using the Instagram API URL but no luck: https://api.instagram.com/oauth/authorize/?client_id=xxx&redirect_uri=xxx&response_type=code
Anyone know how to get the access token completely programmatically in Ruby?
I think I'm missing the step to log in the user (which will be me). Not sure how to do this programatically.
Can I use the instagram API with a code or access token that never changes?
rest-client automatically request to get to redirect host.
https://github.com/rest-client/rest-client/blob/master/lib/restclient/abstract_response.rb
I have never used the instagram API yet, but the code to get "Location" on 302 is shown below.
# client
require 'net/http'
require 'uri'
url = URI.parse('http://localhost:2000')
res = Net::HTTP.start(url.host, url.port) do |http|
http.get('/')
end
puts "#{res.header['Location']} is https://google.com"
# test server
require 'socket'
server = TCPServer.new 2000
loop do
socket = server.accept
while header = socket.gets
break if header.chomp.empty?
puts header.chomp
end
socket.puts "HTTP/1.0 302"
socket.puts "Location: https://google.com"
socket.close
end
I hope that this information is helpful to you.

OpenSSL error when making http request in 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

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

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