Ruby TCPSocket / HTTP request - ruby

I just started with TCPSockets. I am simply trying to get the google home page. This is my code:
require 'socket'
host = 'http://www.google.com'
port = 80
s = TCPSocket.open host, port
s.puts "GET / HTTP/1.1\r\n"
s.puts "Host: Firefox"
s.puts "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
s.puts "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7"
s.puts "\r\n"
while line = s.gets
puts line.chop
end
s.close
This returns:
HTTP/1.1 302 Document has moved
Location: http://92.242.140.29/?nxdomain=http%3A%2F%2Ffirefox&AddInType=2&PlatformInfo=pbrgen
Why? My goal is to get the contents of google home page. Thanks

require 'socket'
host = 'www.google.com'
port = 80
s = TCPSocket.open host, port
s.puts "GET / HTTP/1.1\r\n"
s.puts "\r\n"
while line = s.gets
puts line.chop
end
s.close
Also, using a real HTTP client will make your life much, much easier. I like Typhoeus.

A 302 status is a type of HTTP redirect, but here you're working with TCP, a network layer below HTTP, which doesn't understand redirects (or anything else HTTP). As this SO post shows, howerver, there are other ways to request a web page, namely using the OpenURI library instead of sockets.

Related

Ruby HTTP2 GET request

I'm trying to use the Ruby gem http-2 to send a GET request to Google.
I've lifted the code directly from the example and simplified it slightly:
require 'http/2'
require 'socket'
require 'openssl'
require 'uri'
uri = URI.parse('http://www.google.com/')
tcp = TCPSocket.new(uri.host, uri.port)
sock = tcp
conn = HTTP2::Client.new
conn.on(:frame) do |bytes|
# puts "Sending bytes: #{bytes.unpack("H*").first}"
sock.print bytes
sock.flush
end
conn.on(:frame_sent) do |frame|
puts "Sent frame: #{frame.inspect}"
end
conn.on(:frame_received) do |frame|
puts "Received frame: #{frame.inspect}"
end
stream = conn.new_stream
stream.on(:close) do
puts 'stream closed'
sock.close
end
stream.on(:half_close) do
puts 'closing client-end of the stream'
end
stream.on(:headers) do |h|
puts "response headers: #{h}"
end
stream.on(:data) do |d|
puts "response data chunk: <<#{d}>>"
end
head = {
':scheme' => uri.scheme,
':method' => 'GET',
':path' => uri.path
}
puts 'Sending HTTP 2.0 request'
stream.headers(head, end_stream: true)
while !sock.closed? && !sock.eof?
data = sock.read_nonblock(1024)
# puts "Received bytes: #{data.unpack("H*").first}"
begin
conn << data
rescue => e
puts "#{e.class} exception: #{e.message} - closing socket."
e.backtrace.each { |l| puts "\t" + l }
sock.close
end
end
The output is:
Sending HTTP 2.0 request
Sent frame: {:type=>:settings, :stream=>0, :payload=>[[:settings_max_concurrent_streams, 100]]}
Sent frame: {:type=>:headers, :flags=>[:end_headers, :end_stream], :payload=>[[":scheme", "http"], [":method", "GET"], [":path", "/"]], :stream=>1}
closing client-end of the stream
(Note: you get pretty much the same output as above by running the actual example file, i.e., ruby client.rb http://www.google.com/)
Why is no response data being displayed?
Public servers like google.com do not support HTTP/2 in clear text.
You are trying to connect to http://google.com, while you should really connect to https://google.com (note the https scheme).
In order to do that, you may need to wrap the TCP socket using TLS (see for example here), if http-2 does not do it for you.
Note also that HTTP/2 requires strong TLS ciphers and ALPN, so make sure that you have an updated version of OpenSSL (at least 1.0.2).
Given that the author of http-2 is a strong HTTP/2 supporter, I am guessing that your only problem is the fact that you tried clear-text http rather than https, and I expect that TLS cipher strength and ALPN are taken care of by the http-2 library.

Serving files & handling requests with Webrick on same port

Hi I have a need to be able to receive requests from GitLab (body => JSON) as well as serve files on same port. I am trying to use Webrick for this purpose. I can do these separately.
To serve files I do:
server = WEBrick::HTTPServer.new(:Port => 3030, :DocumentRoot => '/')
server.start
To receive and process jSON I do:
server = WEBrick::HTTPServer.new(:Port => 3030, :DocumentRoot => '/')
server.mount_proc '/' do | req, res |
Queue.new(req.body)
end
But I need this functionality combined, is there a way to do this with Webrick?
Yes, this is certainly possible with Webrick or any HTTP server. There will be two different HTTP actions depending on what the users wants to do, 1.) a GET request to serve the files or 2.) a POST request to process some JSON.
Here's a simple example to show you how to do both:
class Server < WEBrick::HTTPServlet::AbstractServlet
def do_GET (request, response)
puts "this is a get request"
end
def do_POST (request, response)
puts "this is a post request who received #{request.body}"
end
end
server = WEBrick::HTTPServer.new(:Port => 3030)
server.mount "/", Server
trap("INT") {
server.shutdown
}
server.start
Once that is running you can test this by doing the following in a separate terminal window:
curl localhost:3030
output:
this is a get request
localhost - - [23/Apr/2015:06:39:20 EDT] "GET / HTTP/1.1" 200 0
- -> /
To test the POST request:
curl -d "{\"json\":\"payload\"}" localhost:3030
output:
this is a post request who received {"json":"payload"}
localhost - - [23/Apr/2015:06:40:07 EDT] "POST / HTTP/1.1" 200 0
- -> /
Since you mentioned the purpose was a light code-base, here's a light, fast script using the Plezi framework...
This will allow for easier testing, I think (but I'm biased). Also, Plezi is faster on my machine then Webrick (although it's a pure ruby framework, no rack or 'c' extensions involved).
require 'plezi'
class MyController
def index
# parsed JSON is acceible via the params Hash i.e. params[:foo]
# raw JSON request is acceible via request[:body]
# returned response can be set by returning a string...
"The request's params (parsed):\n#{params}\n\nThe raw body:\n#{request[:body]}"
end
end
# start to listen and set the root path for serving files.
listen root: './'
# set a catch-all route so that MyController#index is always called.
route '*', MyController
(if you're running the script from the terminal, remember to exit irb using the exit command - this will activate the web server)

HTTP Server in Ruby with multiple roles

I am struggling to write a script in ruby which should act as a server and initiate further requests to outside party and serve as a server for this party.
I came up with the following:
require 'socket'
require 'net/http'
require 'uri'
server = TCPServer.new('local_ip', 8081)
loop do
# start accepting connections
Thread.start(server.accept) do |client|
request_line = client.gets
request_uri = request_line.split(" ")[1]
path = URI.unescape(URI(request_uri).path)
conn_id = path[-2,2]
sessionID = path[-8,6]
uri = URI("somehost:8000/#{sessionID}/}")
# start another server for accepting communication with server X
mServer = Thread.new {
server = TCPServer.new('local_ip', 8082)
loop do
client = server.accept
response = ""
client.print "HTTP/1.1 200 OK\r\n"
client.print "\r\n"
client.print response
client.close
end
}
# send post request to server X
res = Net::HTTP.post_form(uri, 'connectionid' => conn_id, 'some_param' ==> 'par')
# Here Server X sends me some post data which would be responded
# send subsequent post request to server X
res2 = Net::HTTP.post_form(uri, 'connectionid' => conn_id, 'some_param' ==> 'par2')
client.print "HTTP/1.1 200 OK\r\n"
client.print "\r\n"
client.print response
client.close
end
end
Between two post requests to server X, server X should send post request of its own and when script sends 200 response to server X the subsequent post request to server X should be sent.
I am not entirely sure how to approach synchronization here, I assume I should pause main thread and resume main thread from mServer.
What would be the most optimal approach here? Ideally I would like to separate script into two parts: one accepting connections and sending http requests and second one playing role of mServer. But the synchronization issue remains and I wonder if eventmachine or drb would be appropriate solution.

Ruby TCP server basics

Can someone explain to me what each part of this code is doing?
It would be helpful if someone could give me a step by step explanation.
Also, how could I upload files?
How do I manipulate a ruby server in general?
#!/usr/bin/env ruby
require 'socket'
require 'cgi'
server = TCPServer.new('127.0.0.1', 8888)
puts 'Listening on 127.0.0.1:8888'
loop {
client = server.accept
first_request_header = client.gets
resp = first_request_header
headers = ['http/1.1 200 ok',
"date: #{CGI.rfc1123_date(Time.now)}",
'server: ruby',
'content-type: text/html; charset=iso-8859-1',
"content-length: #{resp.length}\r\n\r\n"].join("\r\n")
client.puts headers # send the time to the client
client.puts resp
client.close
}
#required gems
require 'socket'
require 'cgi'
#creating new connection to a local host on port 8888
server = TCPServer.new('127.0.0.1', 8888)
puts 'Listening on 127.0.0.1:8888'
loop {
#looks like a client method call to open the connection
client = server.accept
first_request_header = client.gets
resp = first_request_header
#setting the request headers
headers = ['http/1.1 200 ok',
"date: #{CGI.rfc1123_date(Time.now)}",
'server: ruby',
'content-type: text/html; charset=iso-8859-1',
"content-length: #{resp.length}\r\n\r\n"].join("\r\n")
#inserts custom client headers into request
client.puts headers
client.puts resp
#closes client connection to local host
client.close
}

Ruby IO from a service at port 6557 in Sinatra

I have to take a dump of a service in sinatra and display it in the content area of the webpage.
The Service I have to access via code runs on server at port 6557. It doesnt use any encryption or authentication. Its a plain readonly request response thingy like http.
Here is what works in teminal
$ echo "GET hosts" | nc 192.168.1.1 6557
gives me the intended output. I need to do something similar using the sinatra application.
I wrote this code but is grossly incorrect. Can sombody help me with code or lookup materials or examples.
get '/' do
host = "192.168.1.1"
port = 6557
dat = ""
#socket = TCPSocket.open (host, port)
while(true)
if(IO.select([],[],[#socket],0))
socket.close
return
end
begin
while( (data = #socket.recv_nonblock(100)) != "")
dat = dat+ data
end
rescue Errno::EAGAIN
end
begin
#str = "GET hosts"
#socket.puts(#str);
rescue Errno::EAGAIN
rescue EOFError
exit
end
IO.select([#socket], [#socket], [#socket])
end
#line = dat
erb :info
end
The code on execution just hangs up.
Also if possible please give some links to read up to get a conceptual context of the problem.
I think the Ruby equivalent to your shell command should be as simple as:
require "socket"
socket = TCPSocket.new "192.168.1.1", 6557
socket.puts "GET hosts"
socket.read
According to the docs, #read should close the socket automatically, so you don't need to worry about doing that manually.
You can execute shell commands directly from ruby using backticks or the system command. Something like this may work for you:
get "/" do
#line = `echo "GET hosts" | nc 192.168.1.1 6557`
erb :info
end
Check out the ruby docs for Kernel#system for more info.

Resources