HTTP Server in Ruby with multiple roles - ruby

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.

Related

Extract uri parameters from a HTTP connection on a Ruby TCPSocket

My first question here... so be gentle :D
I have the following code:
server = TCPServer.new('localhost', 8080)
loop do
socket = server.accept
# Do something with the URL parameters
response = "Hello world";
socket.print response
socket.close
end
The point is that I want to be able to retrieve if any parameters have been sent in URL of the HTTP request.
Example:
From this request:
curl http://localhost:8080/?id=1&content=test
I want to be able to retrieve something like this:
{id => "1", content => "test"}
I've been looking for CGI::Parse[1] or similar solutions but I haven't found a way to extract that data from a TCPSocket.
[1] http://www.ruby-doc.org/stdlib-1.9.3/libdoc/cgi/rdoc/CGI.html#method-c-parse
FYI: My need is to have a minimal http server in order to receive a couple of parameters and wanted to avoid the use of gems and/or full HTTP wrappers/helpers like Rack.
Needless to say... but thanks in advance.
If you want to see a very minimal server, here is one. It handles exactly two parameters, and puts the strings in an array. You'll need to do more to handle variable numbers of parameters.
There is a fuller explanation of the server code at https://practicingruby.com/articles/implementing-an-http-file-server.
require "socket"
server = TCPServer.new('localhost', 8080)
loop do
socket = server.accept
request = socket.gets
# Here is the first line of the request. There are others.
# Your parsing code will need to figure out which are
# the ones you need, and extract what you want. Rack will do
# this for you and give you everything in a nice standard form.
paramstring = request.split('?')[1] # chop off the verb
paramstring = paramstring.split(' ')[0] # chop off the HTTP version
paramarray = paramstring.split('&') # only handles two parameters
# Do something with the URL parameters which are in the parameter array
# Build a response!
# you need to include the Content-Type and Content-Length headers
# to let the client know the size and type of data
# contained in the response. Note that HTTP is whitespace
# sensitive and expects each header line to end with CRLF (i.e. "\r\n")
response = "Hello world!"
socket.print "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: #{response.bytesize}\r\n" +
"Connection: close\r\n"
# Print a blank line to separate the header from the response body,
# as required by the protocol.
socket.print "\r\n"
socket.print response
socket.close
end

Net::HTTP Proxy list

I understand that you could use proxy in the ruby Net::HTTP. However, I have no idea how to do this with a bunch of proxy. I need the Net::HTTP to change to another proxy and send another post request after every post request. Also, is it possible to make the Net::HTTP to change to another proxy if the previous proxy is not working? If so, how?
Code I'm trying to implement the script in:
require 'net/http'
sleep(8)
http = Net::HTTP.new('URLHERE', 80)
http.read_timeout = 5000
http.use_ssl = false
path = 'PATHHERE'
data = '(DATAHERE)'
headers = {
'Referer' => 'REFERER HERE',
'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent' => '(USERAGENTHERE)'}
resp, data = http.post(path, data, headers)
# Output on the screen -> we should get either a 302 redirect (after a successful login) or an error page
puts 'Code = ' + resp.code
puts 'Message = ' + resp.message
resp.each {|key, val| puts key + ' = ' + val}
puts data
end
Given an array of proxies, the following example will make a request through each proxy in the array until it receives a "302 Found" response. (This isn't actually a working example because Google doesn't accept POST requests, but it should work if you insert your own destination and working proxies.)
require 'net/http'
destination = URI.parse "http://www.google.com/search"
proxies = [
"http://proxy-example-1.net:8080",
"http://proxy-example-2.net:8080",
"http://proxy-example-3.net:8080"
]
# Create your POST request_object once
request_object = Net::HTTP::Post.new(destination.request_uri)
request_object.set_form_data({"q" => "stack overflow"})
proxies.each do |raw_proxy|
proxy = URI.parse raw_proxy
# Create a new http_object for each new proxy
http_object = Net::HTTP.new(destination.host, destination.port, proxy.host, proxy.port)
# Make the request
response = http_object.request(request_object)
# If we get a 302, report it and break
if response.code == "302"
puts "#{proxy.host}:#{proxy.port} responded with #{response.code} #{response.message}"
break
end
end
You should also probably do some error checking with begin ... rescue ... end each time you make a request. If you don't do any error checking and a proxy is down, control will never reach the line that checks for response.code == "302" -- the program will just fail with some type of connection timeout error.
See the Net::HTTPHeader docs for other methods that can be used to customize the Net::HTTP::Post object.

How do I read a in incoming POST Multipart request using ruby and tcpserver

I have created a very simple server:
#!/bin/ruby
require 'socket'
server = TCPServer.open 2000
puts "Listening on port 2000"
loop {
client = server.accept
client.puts "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
response = "My super slim ruby http server"
client.puts response
received = client.recv(1024)
puts received
puts "\n"
client.close
}
So far, it serves my purpose, which is to print out the requests that might come from a given client. However, if I use, for example, the following curl command to create a request:
curl -F "data=someData" http://localhost:2000
My ruby server only prints out the HTTP headers, but not the body of the request.
Is there a way to do this?
Looks like you have to call recv again to get the body:
#!/bin/ruby
require 'socket'
server = TCPServer.open 2000
puts "Listening on port 2000"
loop {
client = server.accept
client.puts "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
response = "My super slim ruby http server"
client.puts response
headers = client.recv(1024)
headers =~ /Content-Length: (\d+)/ # get content length
body = $1 ? client.recv($1.to_i) : '' # <- call again to get body if there is one
puts headers + body
client.close
}
Thanks bundacia, I ended up mixing what you sent and some other findings and this is the result:
#!/bin/ruby
require 'socket'
server = TCPServer.open 2000
puts "Listening on port 2000"
loop {
client = server.accept
client.puts "HTTP/1.1 200/OK\r\nContent-type:text/xml\r\n\r\n"
response = "My super slim ruby http server"
client.puts response
all_data = []
i = 1024
firstrun = "yes"
while i > 0
partial_data = client.recv(i)
if (firstrun == "no")
i = 0
end
if (firstrun == "yes")
partial_data =~ /Content-Length: (\d+)/ # get content length
length = $1
if (nil != length && !length.empty?)
i = length.to_i
firstrun = "no"
end
end
all_data << partial_data
end
puts all_data.join()
client.close
}

How to capture POST data from a simple Ruby server

I have a basic Ruby server that I'd like to listen to a specific port, read incoming POST data and do blah...
I have this:
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
client = server.accept # Wait for a client to connect
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
}
How would I go about capturing the POST data?
Thanks for any help.
It's possible to do this without adding much to your server:
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
client = server.accept # Wait for a client to connect
method, path = client.gets.split # In this case, method = "POST" and path = "/"
headers = {}
while line = client.gets.split(' ', 2) # Collect HTTP headers
break if line[0] == "" # Blank line means no more headers
headers[line[0].chop] = line[1].strip # Hash headers by type
end
data = client.read(headers["Content-Length"].to_i) # Read the POST data as specified in the header
puts data # Do what you want with the POST data
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
}
For really simple apps you probably want to write something using Sinatra which is about as basic as you can get.
post('/') do
# Do stuff with post data stored in params
puts params[:example]
end
Then you can stick this in a Rack script, config.ru, and host it easily using any Rack-compliant server.
client.read(length) # length is length of request header content

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
}

Resources