Basic Ruby http server not displaying .jpg to localhost on win7? - ruby

This http script works just fine from my gnome-terminal on Ubuntu (and per Aleksey, on Mac), but on win7, a small square gets loaded in the chrome browser. What do I need to do to get the JPEG sent through local host so it displays in the win7 browser? Per Holger's comment, I need to address the content encoding, but everything I've tried so far makes no difference on win7 (and still loads fine in Ubuntu without any explicit content encoding). ?.
PS C:\Users\user_name\Ruby\http_test> ls
basic_http.rb
lolcat.jpg
PS C:\Users\user_name\Ruby\http_test> ruby basic_http.rb
# very basic http server
require 'socket'
def send_200(socket, content)
socket.puts "HTTP/1.1 200 OK\r\n\r\n#{content}" # <-- Correct? (Per Holger)
socket.close
end
server = TCPServer.new 2016
loop do
Thread.start(server.accept) do |client|
request = client.gets
if request.start_with?("GET")
url = request.split(" ")[1]
if url.start_with?("/images/")
file = url.sub("/images/", "")
picture = File.read(file) # <-- initially Aleksey pointed out
send_200(client, picture) # <-- a variable name mismatch here
else # pictures/picture... heh.
send_200(client, "hello!")
end
end
end
end
FWIW: Ruby 2.2, win7 & coding along with this demo.

You just have a typo in variable name.
You read file to pictures
pictures = File.read(file)
but you send it as picture
send_200(client, picture)
So you just need to edit variable name.
Maybe it would be a good idea to wrap request procession into begin block.
Thread.start(server.accept) do |client|
begin
...
rescue => ex
puts ex
end
end
This way you can see if something goes wrong.

To get the jpeg loaded into the browser on a win7 system, the File.read command needs to explicitly address the binary content encoding, e.g. File.binread("foo.bar") per:
https://ruby-doc.org/core-2.1.0/IO.html#method-c-binread
if url.start_with?("/images/")
file = url.sub("/images/", "")
picture = File.binread(file) # <-- Thank you Holger & Aleksey!!!
send_200(client, picture)
Thanks to Aleksey and Holger!

Related

Ruby and Websockets

I'm trying to create an asynchronous connection to a device that broadcasts packets when something changes, like a door is opened. I want to process the feedback in realtime to the control system. I also need to try to do this with the standard library due to the limitations on some controllers.
Right now I've been using cURL inside ruby to keep a connection open and reconnect if it disconnects. That has worked fine, but on macOS Big Sur after a few days terminal stops working due to the requests. I have not been able to figure out why.
I've rewritten most of my script to use net/http instead of cURL, but I can't figure out keeping a connection open and then real-time sending data to another function.
cURL Ruby Code:
def httpBcast(cmd, url, header,valuesListID,valuesListFID)
fullCommand = "curl -s -k -X #{cmd} '#{url}' #{header}"
loop do
begin
PTY.spawn( fullCommand ) do |stdout, stdin, pid|
begin
# Send Output to Savant
stdout.each { |line| parseBcast(line,valuesListID,valuesListFID)}
rescue Errno::EIO
puts "Errno:EIO error, but this probably just means " +
"that the process has finished giving output"
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
end
end
def parseBcast(msg='',valuesListID,valuesListFID)
if msg.start_with?("data:")
msg.gsub!(/\n+/, '')
msg.gsub!(/\s+/, "")
msg.gsub!("data:","")
msg.to_json
msgP = JSON.parse(msg)
if valuesListFID.include?(msgP['result']['deviceId'])
id = valuesListFID.index(msgP['result']['deviceId'])
id +=1
else
id = "Device Doesn't Exist"
end
msgP['result']['deviceId'] = id
send_savant msgP.to_json
end
end
Any guidance anyone can offer would be most appreciated.
Turns out the vendor needed to make a firmware update. All Good Now.

How do I share object from main file to supporting file in ruby?

I have something similar.
# MAIN.RB
require 'sockets'
require_relative 'replies.rb'
hostname = 'localhost'
port = 6500
s = TCPSocket.open(hostname, port)
$connected = 0
while line = s.gets # Read lines from the socket
#DO A BUNCH OF STUFF
if line == "Hi"
reply line
end
end
s.close
Then I have the reply function in a secondary file.
# REPLIES.RB
def reply(input)
if input == "Hi"
s.write("Hello my friend.\n"
end
end
However calling on the object s from the second file does not seem to work. How would I go about making this work. I'm new to Ruby. I've searched google for the answer, but the only results I have found is with sharing variables across files. I could always do a return "Hello my friend.\n", but I rather be able to write to the socket object directly from the function in REPLIES.rb
Remember that variables are strictly local unless you expressly pass them in. This means s only exists in the main context. You can fix this by passing it in:
reply(s, line)
And on the receiving side:
def reply(s, input)
# ...
end
I'd strongly encourage you to try and indent things consistently here, this code is really out of sorts, and avoid using global variables like $connected. Using a simple self-contained class you could clean up this code considerably.
Also, don't add .rb extensions when calling require. It's implied.

How to FTP in Ruby without first saving the text file

Since Heroku does not allow saving dynamic files to disk, I've run into a dilemma that I am hoping you can help me overcome. I have a text file that I can create in RAM. The problem is that I cannot find a gem or function that would allow me to stream the file to another FTP server. The Net/FTP gem I am using requires that I save the file to disk first. Any suggestions?
ftp = Net::FTP.new(domain)
ftp.passive = true
ftp.login(username, password)
ftp.chdir(path_on_server)
ftp.puttextfile(path_to_web_file)
ftp.close
The ftp.puttextfile function is what is requiring a physical file to exist.
StringIO.new provides an object that acts like an opened file. It's easy to create a method like puttextfile, by using StringIO object instead of file.
require 'net/ftp'
require 'stringio'
class Net::FTP
def puttextcontent(content, remotefile, &block)
f = StringIO.new(content)
begin
storlines("STOR " + remotefile, f, &block)
ensure
f.close
end
end
end
file_content = <<filecontent
<html>
<head><title>Hello!</title></head>
<body>Hello.</body>
</html>
filecontent
ftp = Net::FTP.new(domain)
ftp.passive = true
ftp.login(username, password)
ftp.chdir(path_on_server)
ftp.puttextcontent(file_content, path_to_web_file)
ftp.close
David at Heroku gave a prompt response to a support ticket I entered there.
You can use APP_ROOT/tmp for temporary file output. The existence of files created in this dir is not guaranteed outside the life of a single request, but it should work for your purposes.
Hope this helps,
David

How To Display Characters Received Via A Socket?

I have a very simple Ruby program that acts as an "echo server". When you connect to it via telnet any text you type is echoed back. That part is working. If I add a 'putc' statement to also print each received character on the console running the program only the very first character displayed is printed. After that it continues to echo things back to the telnet client but there is nothing printed on the console.
The following is a small, stripped down program that exhibits the problem.
I am very new to Ruby and have probably made a typical rookie mistake. What did I do wrong?
require 'socket'
puts "Simple Echo Server V1.0"
server = TCPServer.new('127.0.0.1', '2150')
cbuf = ""
while socket = server.accept
cbuf = socket.readchar
socket.putc cbuf
putc cbuf
end
The problem is that your code is only running the while loop once for every time somebody connects (TCPServer#accept accepts a connection). Try something more like:
require 'socket'
puts "Simple Echo Server V1.0"
server = TCPServer.new('127.0.0.1', '2150')
socket = server.accept
while line = socket.readline
socket.puts line
puts line
end

how to code web/application server in ruby?

I need to
run ant remotelly
create/modify xml files for ant
pass back results from ant's execution
so I thought I am going to write a web/application server in ruby. But I do not know where to start.
The computer that will run ant is Win XP SP3 and there is no web server or anything else running.
I found this code but not sure which part to modify so I does what I want. Let's say I want to run "dir" command and send back to the browser result of that command.
require 'socket'
webserver = TCPServer.new('127.0.0.1', 7125)
while (session = webserver.accept)
session.print "HTTP/1.1 200/OK\r\nContent-type:text/html\r\n\r\n"
request = session.gets
trimmedrequest = request.gsub(/GET\ \//, '').gsub(/\ HTTP.*/, '')
filename = trimmedrequest.chomp
if filename == ""
filename = "index.html"
end
begin
displayfile = File.open(filename, 'r')
content = displayfile.read()
session.print content
rescue Errno::ENOENT
session.print "File not found"
end
session.close
end
Ruby includes a Web server (WEBrick) so you don't actually need to use the code that you posted. Sinatra is designed specifically for writing very small Web applications - it lets you write a Web application in a few lines of code and automatically uses the provided Web server.
You can use ruby web server such as Rack, Webrick, mongrel, also you can use Ruby on Rails, Sinatra what else you want.
Of course you can write code from scratch, but it's not good idea to write whole by your own.

Resources