Simple server built on top of Raspbian refuses connections- fix? - ruby

Recently I have gotten a Raspberry Pi, and my first project I decided to build was a simple server that returns 'Hello World' to any and all clients. This is the code:
require 'socket' # Provides TCPServer and TCPSocket classes
puts 'initializing, standby'
server = TCPServer.new('localhost', 2345)
loop do
socket = server.accept
puts 'hello, this is alien'
request = socket.gets
STDERR.puts request
response = "Hello World!\n"
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"
socket.print "\r\n"
socket.print response
socket.close
end
When I run that, the command line outputs initializing, standby. But then when I go to a browser and put in http://localhost:2345/anything, it returns connection refused. Changing localhost to its IP address or hostname does not work either. I have successfully SSHed to the raspberry pi. What is the problem, and how do I fix it?

Your code:
server = TCPServer.new('localhost', 2345)
means the server only listens connections from localhost. You can access it from the pi that is:
curl localhost:2345
But if you want to access the server from outside the pi, the server has to listen connections from anywhere like the following:
server = TCPServer.new('0.0.0.0', 2345)

Related

Eventmachine UDP socket listener does not receive data from network (needs to be "triggered" by localhost data first)

I have two network connected computers sending and receiving UDP data. Sending computer is Windows using SocketTest v 3.0.0
Receiving one is Macbook using this Ruby code for echo server:
require 'eventmachine'
EM.run do
puts "EM.run"
EM.open_datagram_socket '0.0.0.0', 9100 do |server|
puts "socket open"
def server.receive_data(data)
puts "data received: #{data}"
send_data("sending back: #{data}")
end
end
end
When I launch this program and send data from Windows computer, nothing happens. But, when I run this program for a second next to eventmachine echo:
require 'socket'
s = UDPSocket.new
while 1 do
puts "sending..."
s.send "hi", 0, "localhost", 9100
end
Eventmachine prints several "hi" messages as intended, and from now it also receives data from network-connected computer properly (I see "sending back" response on Windows computer).
Why is that? My understanding is that UDP is connectionless, so it should take everything from given port. How signal from "localhost" triggers socket to listen from network here?
OK, so I gave up investigating this and did a workaround:
require 'eventmachine'
require 'socket'
EM.run do
puts "EM.run"
EM.open_datagram_socket '0.0.0.0', 9100 do |server|
# send first packet from localhost to trigger network receiving (bug on my Macbook)
s = UDPSocket.new
s.send Time.now.to_s, 0, "localhost", 9100
def server.receive_data(data)
puts data
send_data("OK")
# forward data to another UDP port
s = UDPSocket.new
s.send data, 0, "localhost", 9101
end
end
end
Ugly, but works. s probably shouldn't be created two times but whatever. Now I can receive network packets in every program on port 9101.

tutorialspoint's simple web browser using tcpsocket

This piece of code supposedly gets the content of any web page:
require 'socket'
host = 'www.tutorialspoint.com' # The web server
port = 80 # Default HTTP port
path = "/index.htm" # The file we want
# This is the HTTP request we send to fetch a file
request = "GET #{path} HTTP/1.0\r\n\r\n"
socket = TCPSocket.open(host,port) # Connect to server
socket.print(request) # Send request
response = socket.read # Read complete response
# Split response at first blank line into headers and body
headers,body = response.split("\r\n\r\n", 2)
puts headers
puts body
When I run it in the command line, I get a 404 Error, but when i go to www.tutorialspoint.com/index.htm it's there, so what gives?:
404 Error Information
Although, I don't have trouble using the open-uri library to get the contents of a web page. But I want to know how to use this one though.
Your request misses the Host parameter:
host = 'www.tutorialspoint.com' # The web server
port = 80 # Default HTTP port
path = "/index.htm" # The file we want
# This is the HTTP request we send to fetch a file
request = "GET #{path} HTTP/1.0\r\nHost: #{host}\r\n\r\n"
Note that apparently not all and every webserver require the "Host:" line (but see comments).

Ruby server logging a socket's request thrice

I am writing a simple server in Ruby in order to understand the Socket module. Here is my code:
require 'socket'
s = TCPServer.new(3939)
loop do
c = s.accept
STDERR.puts c.gets
c.close
end
I simply want to print the request to the server console before closing the socket. Why does it print the request thrice, instead of just once?
If I curl that code
$ curl localhost:3939
I get an empty reply
curl: (52) Empty reply from server
and a single GET request
GET / HTTP/1.1

Ruby TCPServer fails to work sometimes

I've implemented a very simple kind of server in Ruby, using TCPServer. I have a Server class with serve method:
def serve
# Do the actual serving in a child process
#pid = fork do
# Trap signal sent by #stop or by pressing ^C
Signal.trap('INT') { exit }
# Create a new server on port 2835 (1 ounce = 28.35 grams)
server = TCPServer.new('localhost', 2835)
#logger.info 'Listening on http://localhost:2835...'
loop do
socket = server.accept
request_line = socket.gets
#logger.info "* #{request_line}"
socket.print "message"
socket.close
end
end
end
and a stop method:
def stop
#logger.info 'Shutting down'
Process.kill('INT', #pid)
Process.wait
#pid = nil
end
I run my server from the command line, using:
if __FILE__ == $0
server = Server.new
server.logger = Logger.new(STDOUT)
server.logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
begin
server.serve
Process.wait
rescue Interrupt
server.stop
end
end
The problem is that, sometimes, when I do ruby server.rb from my terminal, the server starts, but when I try to make a request on localhost:2835, it fails. Only after several requests it starts serving some pages. In other cases, I need to stop/start the server again for it to properly serve pages. Why is this happening? Am I doing something wrong? I find this very weird...
The same things applies to my specs: I have some specs defined, and some Capybara specs. Before each test I create a server and start it and after each test I stop the server. And the problem persists: tests sometimes pass, sometimes fail because the requested page could not be found.
Is there something fishy going on with my forking?
Would appreciate any answer because I have no more place to look...
Your code is not an HTTP server. It is a TCP server that sends the string "message" over the socket after receiving a newline.
The reason that your code isn't a valid HTTP server is that it doesn't conform to the HTTP protocol. One of the many requirements of the HTTP protocol is that the server respond with a message of the form
HTTP/1.1 <code> <reason>
Where <code> is a number and <reason> is a human-readable "status", like "OK" or "Server Error" or something along those lines. The string message obviously does not conform to this requirement.
Here is a simple introduction to how you might build a HTTP server in ruby: https://practicingruby.com/articles/implementing-an-http-file-server

Ruby webserver without opened port

I am looking for possibility to have ruby-based webserver communicating by pipe, not by TCP/IP. So I will send HTTP request by pipe and I want to read response by pipe. It should be used as bundled/internal webserver (RPC or something) for desktop application. I don't want to handle ports configuration when there will be more instances of my application running on same machine.
Any ideas?
Thank you in advance.
Try a UNIXSocket You use a local path to specify where the socket connection is,
not a port, and you can easily handle multiple simultaneous connections.
# server.rb
require 'socket'
File.delete( filename ) if File.exists? filename
server = UNIXServer.open( filename )
server.listen( queuesize )
puts "waiting on client connection"
while client= server.accept
puts "got client connection #{client.inspect}"
child_pid = fork do
puts "Asking the client what they want"
client.puts "Welcome to your server, what can I get for you?"
until client.eof?
line = client.gets
puts "The client wants #{line.chomp.inspect}"
end
client.close
end
puts "running server (#{child_pid})"
client.close
Process.detach(child_pid)
end
server.close
# client.rb
require 'socket'
puts "requesting server connection"
server = UNIXSocket.new( filename )
puts "got server connection #{server}"
line = server.gets
puts "The server said: #{line.chomp.inspect}"
%w{ a-pony a-puppy a-kitten a-million-dollars }.each do |item|
server.puts item
end
server.close
Pipe is for one-way communication, so there is no way you can set up webserver on that. You might try with unix socket. But really simplest solution is to use loopback (127.0.0.1). It's highly optimized, so the speed won't be a problem.
Not an answer to your question. However, if you do end up having to use a TCP/IP HTTP Server, you should ensure that it's only listening on 127.0.0.1. Listening on the local host address should be quite fast, as it won't hit the network, and will also make it a tad more secure to stop people connecting from the outside.
Thin supports unix sockets.

Resources