EventMachine: I can't seem to pass a args to the Server's constructor (via start_server) or set things via block - ruby

I want to initialize an EventMachine server with an argument, but am having difficulty in doing so.
I have this code that starts a server:
port = APP_CONFIG.user['port']
Display.info "listening on port #{port}"
EM.start_server('0.0.0.0', port, Network::Local::Server) do |server|
server.prepare(_message_dispatcher)
end
which I would expect would call prepare on this class:
module Network
module Local
class Server < EM::Connection
include EM::HttpServer
attr_reader :_message_dispatcher, :_request_processor
def prepare(message_dispatcher) # or initialize(message_dispatcher)
puts 'hi'
#_message_dispatcher = message_dispatcher
#_request_processor = Decryption::RequestProcessor.new(
network: NETWORK_LOCAL,
message_dispatcher: message_dispatcher)
puts 'done'
end
# ....
but the puts never get outputted to the console. I do however see listening on port 8009 from right before I call EM.start_server
I've also tried this:
EM.start_server('0.0.0.0', port,
Network::Local::Server, _message_dispatcher)
and then changed my prepare method to initialize, but I get the same results. The puts don't show in the console, but I do see listening on port 8009.
What am I doing wrong?

Related

Ruby 2.3 TCP Server Daemon for Postfix SMTP Access Policy Delegation

I'm get stuck to write a tcp server daemon in Ruby 2.3. The issue is, that my connection is not going further, when postfix is communicating with the ruby tcp server. If i do connect to the ruby tcp server by telnet, everything works fine. My code is as follows:
require 'socket'
require_relative 'postfix_delegation_object'
class Server
attr_reader :binding, :port
def initialize(binding: '127.0.0.1', port: '1988')
puts "Starting server now!"
puts "Listening on tcp://#{binding}:#{port}"
socket = TCPServer.new(binding, port)
while client = socket.accept
Thread.new { handle_connection(client) }
end
end
def handle_connection(client)
hash_values = {}
puts "New client! #{client}"
while line = client.gets
if line.include? "="
key, val = line.split('=')
hash_values[key] = val.to_s.strip
else
pdo = PostfixDelegationObject.new(hash_values)
client.write("action=dunno")
end
end
end
end
I could solve it by my own. I just had to enter twice '\n'
like this:
client.write("action=dunno\n\n")
This one would not work:
client.write("action=dunno")
client.write("\n\n")

How to run multiple Sinatra instances without anything else?

I want to spawn a lot of configurable Sinatra servers.
For example:
require 'sinatra/base'
class AwesomeOne
def initialize port
#sapp = Sinatra.new {
set :port => port
get '/'do
"Hi!"
end
}
end
def run!
#sapp.run!
end
end
and then:
ths = []
(1111..9999).each { |port|
ths.push Thread.new { AwesomeOne.new(port).run! }
}
But something goes wrong: i can't access each page. But some of them seems accessible.
So, how to run Sinatra multiple times in one .rb file?
I happen to need the same thing soon so I did some research.
You could use Sinatra's cascade, routing or middleware options, see https://www.safaribooksonline.com/library/view/sinatra-up-and/9781449306847/ch04.html and search for 'multiple', I advise you to buy the book and read it, it is all very usefull stuff !
But more leaning to your approach you can use the eventmachine Sinatra allready uses to run multiple apps on different ports, Ruby itself is started only once.
See http://recipes.sinatrarb.com/p/embed/event-machine for explanation and more examples. I combined the example with your code.
# adapted from http://recipes.sinatrarb.com/p/embed/event-machine
require 'eventmachine'
require 'sinatra/base'
require 'thin'
def run(opts)
EM.run do
server = opts[:server] || 'thin'
host = opts[:host] || '0.0.0.0'
port = opts[:port] || '8181'
web_app = opts[:app]
dispatch = Rack::Builder.app do
map '/' do
run web_app
end
end
unless ['thin', 'hatetepe', 'goliath'].include? server
raise "Need an EM webserver, but #{server} isn't"
end
Rack::Server.start({
app: dispatch,
server: server,
Host: host,
Port: port,
signals: false,
})
end
end
class HelloApp < Sinatra::Base
configure do
set :threaded, true
end
get '/hello' do
"Hello World from port #{request.port}"
end
end
ths = []
(4567..4569).each do |port|
ths.push Thread.new { run app: HelloApp.new, port: port }
end
ths.each{|t| t.join}
output
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 0.0.0.0:4567, CTRL+C to stop
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 0.0.0.0:4568, CTRL+C to stop
Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on 0.0.0.0:4569, CTRL+C to stop
In windows cmd netstat -ab this gives
TCP 0.0.0.0:4567 ******:0 LISTENING
TCP 0.0.0.0:4568 ******:0 LISTENING
TCP 0.0.0.0:4569 ******:0 LISTENING
And the hello example on all ports works.

Multiple servers in a single EventMachine reactor

Is it possible to run multiple servers within a single event machine?
What I mean is that multiple services can be used concurrently by a single client connection. For example, a login server authenticates a user and then a user can use both a chat room and a simple game such as checkers with a single client socket concurrently?
Or do I need multiple eventmachine reactors for each service?
I tried this and it's working:
#!/usr/bin/env ruby
require 'eventmachine'
module EchoServer
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data data
send_data ">>>you sent: #{data}"
close_connection if data =~ /quit/i
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
EventMachine::run {
EventMachine::start_server "127.0.0.1", 8081, EchoServer
EventMachine::start_server "127.0.0.1", 8082, EchoServer
EventMachine::start_server "127.0.0.1", 8083, EchoServer
}
Here you get 3 echo services with different ports. (I was too lazy to implement different services.)
So, it's very easy to build a huge multi service wrapper.
Update
Simple code example for condition based start of a EM server:
#!/usr/bin/env ruby
# encoding: utf-8
require 'eventmachine'
module EchoServer
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data data
send_data ">>>you sent: #{data}"
close_connection if data =~ /quit/i
end
def unbind
puts "-- someone disconnected from the echo server!"
end
end
$check_ok = false
EventMachine::run {
puts "checker var is: #{$check_ok}"
EventMachine::start_server "127.0.0.1", 8081, EchoServer
EventMachine::start_server "127.0.0.1", 8082, EchoServer
puts "echos on 8081 and 8082 started."
# periodic timer check - every 1 sec
EventMachine.add_periodic_timer(1) {
if $check_ok
EventMachine::start_server "127.0.0.1", 8083, EchoServer
$check_ok = false
puts "echo on 8083 started!"
end
}
# timer triggered after 10 secs - only once!
EventMachine.add_timer(10) {
$check_ok = true
puts "checker var is #{$check_ok} now!"
}
}
In this example the echo server on port 8083 is started ~10 secs after app start. Try to telnet localhost 8083 before and after this timer, you'll see the effect.
You also can use values lower than 1 sec like 0.01 for every 1/100th sec checks.
This might be your starting point for your own ideas. The periodic timer is your internal loop, where you hook in your conditional checks for starting further services.
Good tutorial (PDF): eventmachine introduction (blog post)

eventmachine server failing to execute receive_data

I have an eventmachine app where one script is reading from a file, sending data to another script line by line, and the "server" script is acting upon that data. Unfortunately, the "server" script fails to execute receive_data as it should. I know that a connection is being made because it eecutes post_init, and I know the sender script is sending data. Here is some of my code along with how I start the server.
module BT_Server
def post_init
puts "-- someone connected to the echo server!"
end
def receive_data(data)
puts "hi"
int, time, *int_macs = data.split("-")
# more stuff that isn't needed here
end
def bt_left(dev)
dev.save
if t = Device.macs.index(dev.mac)
Device.all[t].add_int(dev.int, dev.t_0, dev.t_l)
else
Device.new(dev.mac, dev.int, dev.t_0, dev.t_l)
end
return false
end
def unbind
puts "disconnection"
end
end
EventMachine::run {
EventMachine::start_server 'localhost', 8081, BT_Server
puts t_0 = Time.new
puts 'listening...'
}
Note: I have the Module definition in a separate file, along with my classes, which I require into the server script, if that makes any difference.
i tested your code and it outputs 'hi' every time i send something via telnet.
from my point of view, the code is correct.
are you sure the sending script is working? try with a manual telnet on port 8081.
regards.

Running a loop (such as one for a mock webserver) within a thread

I'm trying to run a mock webserver within a thread within a class. I've tried passing the class' #server property to the thread block but as soon as I try to do server.accept the thread stops. Is there some way to make this work? I want to basically be able to run a webserver off of this script while still taking user input via stdin.gets. Is this possible?
class Server
def initialize()
#server = TCPServer.new(8080)
end
def run()
#thread = Thread.new(#server) { |server|
while true
newsock = server.accept
puts "some stuff after accept!"
next if !newsock
# some other stuff
end
}
end
end
def processCommand()
# some user commands here
end
test = Server.new
while true do
processCommand(STDIN.gets)
end
In the above sample, the thread dies on server.accept
In the code you posted, you're not calling Server#run. That's probably just an oversight in making the post. Server.accept is supposed to block a thread, returning only when someone has connected.
Anyone who goes into writing an HTTP server with bright eyes soon learns that it's more fun to let someone else do that work. For quick and dirty HTTP servers, I've got good results enlisting the aid of WEBrick. It's a part of the Ruby library. Here's a WEBrick server that will serve up "Boo!" When you connect your browser to localhost:8080/:
#!/usr/bin/ruby1.8
require 'webrick'
class MiniServer
def initialize
Thread.new do
Thread::abort_on_exception = true
server = WEBrick::HTTPServer.new(:BindAddress=>'127.0.0.1',
:Port=>8080,
:Logger=>WEBrick::Log.new('/dev/stdout'))
server.mount('/', Servlet, self)
server.start
end
end
private
class Servlet < WEBrick::HTTPServlet::AbstractServlet
def initialize(webrick_server, mini_server)
end
def do_GET(req, resp)
resp.body = "<html><head></head><body>Boo!</body></html>"
end
alias :do_POST :do_GET
end
end
server = MiniServer.new
gets
I don't know ruby, but it looks like server.accept is blocking until you get a tcp connection... your thread will continue as soon as a connection is accepted.
You should start the server in your main thread and then spawn a new thread for each connection that you accept, that way your server will immediately go to accept another connection and your thread will service the one that was just accepted.

Resources