So witness and observe the following code, my questions is why do i never make it to the on_connect after starting the cool.io loop in send_to_server, the l.run should fire off the request as per the documented example on the github, and how the code handles incoming connections in module Server #socket.attach(l)
l.run
which does work and accepts the incoming data and sends it to my parser, which does work and fires off all the way up until the aforementioned send_to_server. So what is going on here?
require 'cool.io'
require 'http/parser'
require 'uri'
class Hash
def downcase_key
keys.each do |k|
store(k.downcase, Array === (v = delete(k)) ? v.map(&:downcase_key) : v)
end
self
end
end
module ShadyProxy
extend self
module ClientParserCallbacks
extend self
def on_message_complete(conn)
lambda do
puts "on_message_complete"
PluginHooks.before_request_to_server(conn)
end
end
def on_headers_complete(conn)
lambda do |headers|
conn.headers = headers
end
end
def on_body(conn)
lambda do |chunk|
conn.body << chunk
end
end
end
module PluginHooks
extend self
def before_request_to_server(conn)
# modify request here
conn.parser.headers.delete "Proxy-Connection"
conn.parser.headers.downcase_key
send_to_server(conn)
end
def send_to_server(conn)
parser = conn.parser
uri = URI::parse(parser.request_url)
l = Coolio::Loop.default
puts uri.scheme + "://" + uri.host
c = ShadyHttpClient.connect(uri.scheme + "://" + uri.host,uri.port).attach(l)
c.connection_reference = conn
c.request(parser.http_method,uri.request_uri)
l.run
end
def before_reply_to_client(conn)
end
end
class ShadyHttpClient < Coolio::HttpClient
def connection_reference=(conn)
puts "haz conneciton ref"
#connection_reference = conn
end
def connection_reference
#connection_reference
end
def on_connect
super
#never gets here
#headers = nil
#body = ''
#buffer = ''
end
def on_connect_failed
super
# never gets here either
end
def on_response_header(header)
#headers = header
end
def on_body_data(data)
puts "on data?"
#body << data
STDOUT.write data
end
def on_request_complete
puts "Headers"
puts #headers
puts "Body"
puts #body
end
def on_error(reason)
STDERR.puts "Error: #{reason}"
end
end
class ShadyProxyConnection < Cool.io::TCPSocket
attr_accessor :headers, :body, :buffer, :parser
def on_connect
#headers = nil
#body = ''
#buffer = ''
#parser = Http::Parser.new
#parser.on_message_complete = ClientParserCallbacks.on_message_complete(self)
#parser.on_headers_complete = ClientParserCallbacks.on_headers_complete(self)
#parser.on_body = ClientParserCallbacks.on_body(self)
end
def on_close
puts "huh?"
end
def on_read(data)
#buffer << data
#parser << data
end
end
module Server
def run(opts)
begin
# Start our server to handle connections (will raise things on errors)
l = Coolio::Loop.new
#socket = Cool.io::TCPServer.new(opts[:host],opts[:port], ShadyProxy::ShadyProxyConnection)
#socket.attach(l)
l.run
# Handle every request in another thread
loop do
Thread.new s = #socket.accept
end
# CTRL-C
rescue Interrupt
puts 'Got Interrupt..'
# Ensure that we release the socket on errors
ensure
if #socket
#socket.close
puts 'Socked closed..'
end
puts 'Quitting.'
end
end
module_function :run
end
end
ShadyProxy::Server.run(:host => '0.0.0.0',:port => 1234)
Related
I have a plain ruby class Espresso::MyExampleClass.
module Espresso
class MyExampleClass
def my_first_function(value)
puts "my_first_function"
end
def my_function_to_run_before
puts "Running before"
end
end
end
With some of the methods in the class, I want to perform a before or after callback similar to ActiveSupport callbacks before_action or before_filter. I'd like to put something like this in my class, which will run my_function_to_run_before before my_first_function:
before_method :my_function_to_run_before, only: :my_first_function
The result should be something like:
klass = Espresso::MyExampleClass.new
klass.my_first_function("yes")
> "Running before"
> "my_first_function"
How do I use call backs in a plain ruby class like in Rails to run a method before each specified method?
Edit2:
Thanks #tadman for recommending XY problem. The real issue we have is with an API client that has a token expiration. Before each call to the API, we need to check to see if the token is expired. If we have a ton of function for the API, it would be cumbersome to check if the token was expired each time.
Here is the example class:
require "rubygems"
require "bundler/setup"
require 'active_support/all'
require 'httparty'
require 'json'
module Espresso
class Client
include HTTParty
include ActiveSupport::Callbacks
def initialize
login("admin#example.com", "password")
end
def login(username, password)
puts "logging in"
uri = URI.parse("localhost:3000" + '/login')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data(username: username, password: password)
response = http.request(request)
body = JSON.parse(response.body)
#access_token = body['access_token']
#expires_in = body['expires_in']
#expires = #expires_in.seconds.from_now
#options = {
headers: {
Authorization: "Bearer #{#access_token}"
}
}
end
def is_token_expired?
#if Time.now > #expires.
if 1.hour.ago > #expires
puts "Going to expire"
else
puts "not going to expire"
end
1.hour.ago > #expires ? false : true
end
# Gets posts
def get_posts
#Check if the token is expired, if is login again and get a new token
if is_token_expired?
login("admin#example.com", "password")
end
self.class.get('/posts', #options)
end
# Gets comments
def get_comments
#Check if the token is expired, if is login again and get a new token
if is_token_expired?
login("admin#example.com", "password")
end
self.class.get('/comments', #options)
end
end
end
klass = Espresso::Client.new
klass.get_posts
klass.get_comments
A naive implementation would be;
module Callbacks
def self.extended(base)
base.send(:include, InstanceMethods)
end
def overridden_methods
#overridden_methods ||= []
end
def callbacks
#callbacks ||= Hash.new { |hash, key| hash[key] = [] }
end
def method_added(method_name)
return if should_override?(method_name)
overridden_methods << method_name
original_method_name = "original_#{method_name}"
alias_method(original_method_name, method_name)
define_method(method_name) do |*args|
run_callbacks_for(method_name)
send(original_method_name, *args)
end
end
def should_override?(method_name)
overridden_methods.include?(method_name) || method_name =~ /original_/
end
def before_run(method_name, callback)
callbacks[method_name] << callback
end
module InstanceMethods
def run_callbacks_for(method_name)
self.class.callbacks[method_name].to_a.each do |callback|
send(callback)
end
end
end
end
class Foo
extend Callbacks
before_run :bar, :zoo
def bar
puts 'bar'
end
def zoo
puts 'This runs everytime you call `bar`'
end
end
Foo.new.bar #=> This runs everytime you call `bar`
#=> bar
The tricky point in this implementation is, method_added. Whenever a method gets bind, method_added method gets called by ruby with the name of the method. Inside of this method, what I am doing is just name mangling and overriding the original method with the new one which first runs the callbacks then calls the original method.
Note that, this implementation neither supports block callbacks nor callbacks for super class methods. Both of them could be implemented easily though.
I'm working on my first attempts on Sockets and Threading and am running into an issue where I believe I am hitting a thread cap, but not entirely sure.
Basically I have a server.rb which opens a TCPServer and a game.rb which connects to the server. On the server, when the user connects I output some general information and then want to start reading data that is passed through from the game.rb. I believe the problem I am running into is I am just creating a new thread everytime the loop executes and I think I'm meeting the cap. I am also not sure if this is best practice. I wanted to avoid using other APIs until I got a decent grasp on the basics, so I tried to follow the code from https://github.com/sausheong/tanks.
server.rb
require 'socket'
# lsof -i :2000
# kill ID
class Server
def initialize(host, port)
puts "starting arena"
#server = TCPServer.open(host, port)
#sprites = Hash.new
#players = Hash.new
handle_connection(#server.accept)
end
def handle_connection(socket)
_, port, host = socket.peeraddr
user = "#{host}:#{port}"
puts "#{user} has joined!"
puts "------"
loop do
thread = Thread.start(socket) do |client|
puts 'starting new thread'
data = socket.readpartial(4096)
data_array = data.split("\n")
if data_array && data_array.any?
begin
data_array.each do |row|
message = row.split("|")
puts message
end
rescue Exception => exception
puts "exception happened?"
puts exception.inspect
end
end
end
end # end loop
rescue EOFError => err
puts "error"
puts "Closing the connection. Bye!"
socket.close
puts err
end # handle_connection end
end # class end
Server.new('localhost', 2000)
game.rb
# create a server
# join server
# move Player
# communicate to server that player moved
# update players position for server?
require 'gosu'
require 'socket'
class Player
def initialize
#image = Gosu::Image.new("media/starfighter.bmp")
#x = #y = #vel_x = #vel_y = #angle = 0.0
#score = 0
end
def warp(x, y)
#x, #y = x, y
end
def turn_left
#angle -= 4.5
end
def turn_right
#angle += 4.5
end
def accelerate
#vel_x += Gosu.offset_x(#angle, 0.5)
#vel_y += Gosu.offset_y(#angle, 0.5)
end
def move
#x += #vel_x
#y += #vel_y
#x %= 640
#y %= 480
#vel_x *= 0.95
#vel_y *= 0.95
end
def draw
#image.draw_rot(#x, #y, 1, #angle)
end
end
class Client
def initialize(host, port)
#client = TCPSocket.open(host, port)
send_data("321|123")
end
def send_data(data)
#client.write(data)
end
# puts "enter your name:"
# client.write gets
#
# while line = client.gets
# puts line.chop
# end
end
class Tutorial < Gosu::Window
def initialize(server, port)
super 640, 480
self.caption = "Tutorial Game"
#client = Client.new(server, port)
#background_image = Gosu::Image.new("media/space.png", :tileable => true)
#player = Player.new
#player.warp(320, 240)
end
def update
if Gosu.button_down? Gosu::KB_LEFT or Gosu::button_down? Gosu::GP_LEFT
#player.turn_left
#client.send_data("left")
end
if Gosu.button_down? Gosu::KB_RIGHT or Gosu::button_down? Gosu::GP_RIGHT
#player.turn_right
end
if Gosu.button_down? Gosu::KB_UP or Gosu::button_down? Gosu::GP_BUTTON_0
#player.accelerate
end
#player.move
end
def draw
#player.draw
#background_image.draw(0, 0, 0)
end
def button_down(id)
if id == Gosu::KB_ESCAPE
close
else
super
end
end
end
Tutorial.new('localhost', 2000).show
Your loop: (simplified)
socket = #server.accept
loop do
Thread.start(socket) do |client|
# ...
end
end
accepts a single connection and then creates new threads as fast as it can, passing each thread the same socket object, so all of the threads start reading data from the same socket concurrently.
To avoid this, you have to move the accept call into the loop:
loop do
socket = #server.accept
Thread.start(socket) do |client|
# ...
end
end
Or simply:
loop do
Thread.start(#server.accept) do |client|
# ...
end
end
accept immediately blocks to wait for a new connection, effectively pausing the loop. Once a connection is established, it creates a new thread to handle that connection and the loop starts over.
A similar example can be found in the documentation for TCPServer.
I am attempting to write a chat server with EventMachine. How do I pass a message from one EventMachine connection, to another, in a thread-safe manner?
I see a messaging protocol (Stomp) being supported but I can't figure out how to use it. Any help is appreciated.
Stomp in EventMachine - http://eventmachine.rubyforge.org/EventMachine/Protocols/Stomp.html
See http://eventmachine.rubyforge.org/EventMachine/Channel.html
you can try something in these lines:
require 'eventmachine'
class Chat < EventMachine::Connection
def initialize channel
#channel = channel
end
def post_init
send_data 'Hello'
#sid = #channel.subscribe do |msg|
send_data msg
end
end
def receive_data(msg)
#channel.push msg
end
def unbind
#channel.unsubscribe #sid
end
end
EM.run do
#channel = EventMachine::Channel.new
EventMachine.start_server '127.0.0.1', 8081, Chat, #channel
end
EDIT: also check out https://github.com/eventmachine/eventmachine/tree/master/examples/guides/getting_started - there is a nice chatroom example
Try starting out with an in memory message dispatcher.
require 'thread'
class Room
def initialize
#users = []
end
def join(user)
#users << user
end
def leave(user)
#user.delete(user)
end
def broadcast(message)
#users.each do |user|
user.enqueue(message)
end
end
end
class User
def initialize
#mutex = Mutex.new
#queued_messages = []
end
def enqueue(message)
#mutex.synchronize do
#queued_message << message
end
end
def get_new_messages
#mutex.synchronize do
output = #queued_messages
#queued_messages = []
end
return output
end
end
UPDATE
ROOM = Room.new
class Connection
def user_logged_in
# #user = ...
ROOM.join(#user)
end
def received_message(message)
ROOM.broadcast(message)
end
def receive_send_more_messages_request(req)
messages = #user.get_new_messages
# write messages
end
def connection_closed
ROOM.leave(#user)
end
end
I would like to upload data I generated at runtime in Ruby, something like feeding the upload from a block.
All examples I found only show how to stream a file that must be on disk prior to the request but I do not want to buffer the file.
What is the best solution besides rolling my own socket connection?
This a pseudocode example:
post_stream('127.0.0.1', '/stream/') do |body|
generate_xml do |segment|
body << segment
end
end
Code that works.
require 'thread'
require 'net/http'
require 'base64'
require 'openssl'
class Producer
def initialize
#mutex = Mutex.new
#body = ''
#eof = false
end
def eof!()
#eof = true
end
def eof?()
#eof
end
def read(size)
#mutex.synchronize {
#body.slice!(0,size)
}
end
def produce(str)
if #body.empty? && #eof
nil
else
#mutex.synchronize { #body.slice!(0,size) }
end
end
end
data = "--60079\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test.file\"\r\nContent-Type: application/x-ruby\r\n\r\nthis is just a test\r\n--60079--\r\n"
req = Net::HTTP::Post.new('/')
producer = Producer.new
req.body_stream = producer
req.content_length = data.length
req.content_type = "multipart/form-data; boundary=60079"
t1 = Thread.new do
producer.produce(data)
producer.eof!
end
res = Net::HTTP.new('127.0.0.1', 9000).start {|http| http.request(req) }
puts res
There's a Net::HTTPGenericRequest#body_stream=( obj.should respond_to?(:read) )
You use it more or less like this:
class Producer
def initialize
#mutex = Mutex.new
#body = ''
end
def read(size)
#mutex.synchronize {
#body.slice!(0,size)
}
end
def produce(str)
#mutex.synchronize {
#body << str
}
end
end
# Create a producer thread
req = Net::HTTP::Post.new(url.path)
req.body_stream = producer
res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
I have a testing library to assist in testing logging:
require 'stringio'
module RedirectIo
def setup
$stderr = #stderr = StringIO.new
$stdin = #stdin = StringIO.new
$stdout = #stdout = StringIO.new
super
end
def teardown
$stderr = STDERR
$stdin = STDIN
$stdout = STDOUT
super
end
end
It is meant to be used like so:
require 'lib/redirect_io'
class FooTest < Test::Unit::TestCase
include RedirectIo
def test_logging
msg = 'bar'
Foo.new.log msg
assert_match /^#{TIMESTAMP_REGEX} #{msg}$/, #stdout.string, 'log message'
end
end
Of course, I have a unit test for my test library. :)
require 'lib/redirect_io'
class RedirectIoTest < Test::Unit::TestCase
def test_setup_and_teardown_are_mixed_in
%W{setup teardown}.each do |method|
assert_not_equal self.class, self.class.new(__method__).method(method).owner, "owner of method #{method}"
end
self.class.class_eval { include RedirectIo }
%W{setup teardown}.each do |method|
assert_equal RedirectIo, self.class.new(__method__).method(method).owner, "owner of method #{method}"
end
end
def test_std_streams_captured
%W{STDERR STDIN STDOUT}.each do |stream_name|
assert_equal eval(stream_name), self.class.new(__method__).instance_eval("$#{stream_name.downcase}"), stream_name
end
self.class.class_eval { include RedirectIo }
setup
%W{STDERR STDIN STDOUT}.each do |stream_name|
assert_not_equal eval(stream_name), self.class.new(__method__).instance_eval("$#{stream_name.downcase}"),
stream_name
end
teardown
%W{STDERR STDIN STDOUT}.each do |stream_name|
assert_equal eval(stream_name), self.class.new(__method__).instance_eval("$#{stream_name.downcase}"), stream_name
end
end
end
I'm having trouble figuring out how to test that RedirectIo.setup and teardown call super. Any ideas?
I figured out one way to do it, which actually makes the test code much cleaner as well!
require 'lib/redirect_io'
class RedirectIoTest < Test::Unit::TestCase
class RedirectIoTestSubjectSuper < Test::Unit::TestCase
def setup
#setup = true
end
def teardown
#teardown = true
end
end
class RedirectIoTestSubject < RedirectIoTestSubjectSuper
include RedirectIo
def test_fake; end
end
def test_setup_and_teardown_are_mixed_in
%W{setup teardown}.each do |method|
assert_not_equal RedirectIoTestSubject, self.method(method).owner, "owner of method #{method}"
end
%W{setup teardown}.each do |method|
assert_equal RedirectIo, RedirectIoTestSubject.new(:test_fake).method(method).owner, "owner of method #{method}"
end
end
def test_std_streams_captured
obj = RedirectIoTestSubject.new(:test_fake)
$stderr = STDERR
$stdin = STDIN
$stdout = STDOUT
obj.setup
%W{STDERR STDIN STDOUT}.each do |stream_name|
assert_not_equal eval(stream_name), eval("$#{stream_name.downcase}"), stream_name
end
obj.teardown
%W{STDERR STDIN STDOUT}.each do |stream_name|
assert_equal eval(stream_name), eval("$#{stream_name.downcase}"), stream_name
end
end
def test_setup_and_teardown_call_super
obj = RedirectIoTestSubject.new(:test_fake)
obj.setup
assert obj.instance_eval{#setup}, 'super called?'
obj.teardown
assert obj.instance_eval{#teardown}, 'super called?'
end
end