kenichi / angelo Goto Github PK
View Code? Open in Web Editor NEWSinatra-like DSL for Reel that supports WebSockets and SSE
License: Other
Sinatra-like DSL for Reel that supports WebSockets and SSE
License: Other
At least twice Angelo crashed with the following stack trace:
E, [2014-12-01T07:36:36.555571 #9954] ERROR -- : Actor crashed!
Reel::StateError: already processing a request
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/reel-0.5.0/lib/reel/connection.rb:55:in `request'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/reel-0.5.0/lib/reel/connection.rb:72:in `each_request'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/angelo-0.2.4/lib/angelo/server.rb:23:in `on_connection'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/reel-0.5.0/lib/reel/server.rb:56:in `call'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/reel-0.5.0/lib/reel/server.rb:56:in `handle_connection'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `public_send'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `dispatch'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/calls.rb:122:in `dispatch'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/cell.rb:60:in `block in invoke'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/cell.rb:71:in `block in task'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/actor.rb:357:in `block in task'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/tasks.rb:57:in `block in initialize'
/home/koala/.rvm/gems/ruby-2.1.0@ufficio/gems/celluloid-0.16.0/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'
It seems somehow related to those Reel issues: celluloid/reel#150 and celluloid/reel#146
Do you have any idea?
(Yes, I did not update yet that machine to Angelo 0.3.2, but AFAIK you did not change anything about request management... I'm going to update it asap, anyway)
When an invalid JSON string is sent to the server, the function parse_post_body raises a ParseError and it crashes.
I think the best approach would be sending back to the client an halt 400
(BadRequest).
To do so we have a couple of ways:
params
evaluation eager and catch the exceptionProbably the latter is a bit better because it will make more reliable every Angelo instance without overhead.
What do you think?
Does angelo support file uploads? Im trying to send file from Rails apllication to angelo server with form like
<form method="POST" enctype="multipart/form-data" action="http://127.0.0.1:4567/images">
<div>
<input name="image" type="file" />
</div>
<div>
<input type="submit" value="Submit" />
</div>
</form>
server code:
class SampleServer < Angelo::Base
post '/images' do
puts request.inspect
puts params
end
end
and log:
I, [2015-02-13T11:18:10.885547 #4130] INFO -- : 127.0.0.1 - - "POST /images HTTP/1.1" 200 5
#<Reel::Request POST /images HTTP/1.1 @headers={"Host"=>"127.0.0.1:4567", "Connection"=>"keep-alive", "Content-Length"=>"3437206", "Cache-Control"=>"max-age=0", "Accept"=>"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Origin"=>"http://localhost:3000", "User-Agent"=>"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36", "Content-Type"=>"multipart/form-data; boundary=----WebKitFormBoundaryMMXfV2Ron4mihT2p", "Referer"=>"http://localhost:3000/", "Accept-Encoding"=>"gzip, deflate", "Accept-Language"=>"en-US,en;q=0.8"}>
{}
How to be starting with for example bundle exec ruby foo.rb -p $PORT
?
Sinatra has a great module for allowing you to rename your route names while still maintaining backward compatibility for older clients:
Last night my server was killed by the OS because it was using too much memory.
Try this simple code watching the memory usage.
require 'angelo'
class AngeloStressTest < Angelo::Base
get '/' do
'hello ' * 10E5
end
get '/gc/' do
GC.start
'gc started'
end
end
s = AngeloStressTest.run!
After every request the memory usage increases and even invoking the garbage collector it never returns to the initial (more or less 33 MB on my system) or the previous value.
I've been using Angelo for a while now and I just noticed it doesn't support the PATCH HTTP verb. Is this on purpose? I can try submitting a PR but wanted to check first.
Hi,
i have a little angelo based service which implements a POST request with content_type :json
. As data an array is given.
So when i
curl -s -v -X POST -H "Content-type:application/json" -d '["1451","1452"]' "http://0.0.0.0:3005/do_sth"
it crashes with the following stacktrace:
/Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/params_parser.rb:64:in 'merge!' /Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/params_parser.rb:64:in 'initialize' /Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/params_parser.rb:29:in 'new' /Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/params_parser.rb:29:in 'parse_post_body' /Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/params_parser.rb:36:in 'parse_query_string_and_post_body' /Users/gunnar/.rvm/gems/ruby-2.1.5@sa_media_service/gems/angelo-0.4.1/lib/angelo/base.rb:241:in 'params'
So the params_parser always expects a hash as post_body and crashes otherwise. Though an array should be valid json too.
this commit breaks the "main" style feature added by @tommay because it makes Forwardable
call method_defined?
which is not present on main
.
tried replacing the forward/def_delegators stuff in lib/angelo/main.rb
with this but am running into errors i'm not understanding immediately:
Angelo::Base::DSL.instance_methods.each do |bim|
define_method bim do |*a|
@angelo_app.__send__ bim, *a
end
end
Having gotten used to writing REST APIs in Node.js with Express, I'm accustomed to being able to kick off asynchronous tasks when an endpoint is hit and not send the HTTP response until a callback is called. I've failed to find any documentation that gives insight into how to do something similar with Angelo (or the underlying Reel/Celluloid).
With the documented tasks
and async
/futures
, it seems that either the caller can't get the result of the task (async) or the caller can get the result of the task at the cost of blocking all other execution until the task is complete (futures). As a result, neither method seems to suit my goal of doing some non-blocking calculation or IO when an endpoint is hit and not returning a response to the client until the calculation is complete. It seems that my goal is not possible with Angelo. Is this correct?
Please feel free to close this issue if you feel it's irrelevant, I wasn't sure how else to get in contact with the Angelo community.
Issuing a Ctrl+C to shutdown the server results in a stack dump.
Environment details:
C:\src\exlite\mockEo>jruby -v
jruby 9.0.0.0.rc1 (2.2.2) 2015-06-10 a0bf3b3 Java HotSpot(TM) 64-Bit Server VM 24.79-b02 on 1.7.0_79-b15 +jit [Windows 7-amd64]
C:\src\exlite\mockEo>jruby -S gem
*** LOCAL GEMS ***
angelo (0.4.1)
...
reel (0.5.0)
...
Crash message:
C:\src\exlite\mockEo>jruby web_server.rb
I, [2015-06-15T09:21:25.229000 #7940] INFO -- : Angelo 0.4.1
I, [2015-06-15T09:21:25.229000 #7940] INFO -- : listening on localhost:4566
I, [2015-06-15T09:22:01.676000 #7940] INFO -- : 127.0.0.1 - - "POST /bid_set HTTP/1.1" 404 -
^C
C:\src\exlite\mockEo>W, [2015-06-15T09:23:32.853000 #7940] WARN -- : Terminating task: type=:call, meta={:method_name=>:run}, statu
s=:iowait
Celluloid::TaskFiber backtrace unavailable. Please try `Celluloid.task_class = Celluloid::TaskThread` if you need backtraces
here.
E, [2015-06-15T09:23:32.869000 #7940] ERROR -- : CLEANUP CRASHED!
Java::JavaLang::NullPointerException:
org.jruby.util.io.OpenFile.channel(OpenFile.java:2231)
org.jruby.RubyIO.getChannel(RubyIO.java:403)
org.nio4r.Nio4r$Selector.deregister(Nio4r.java:188)
org.nio4r.Nio4r$Selector$INVOKER$i$1$0$deregister.call(Nio4r$Selector$INVOKER$i$1$0$deregister.gen)
org.jruby.RubyClass.finvoke(RubyClass.java:746)
org.jruby.runtime.Helpers.invoke(Helpers.java:416)
org.jruby.RubyBasicObject.callMethod(RubyBasicObject.java:374)
org.nio4r.Nio4r$Monitor.close(Nio4r.java:422)
org.nio4r.Nio4r$Monitor.close(Nio4r.java:413)
org.nio4r.Nio4r$Monitor$INVOKER$i$close.call(Nio4r$Monitor$INVOKER$i$close.gen)
org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:293)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:131)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:308)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:129)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:115)
org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:77)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
org.jruby.ir.instructions.CallBase.interpret(CallBase.java:419)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:324)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:84)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:199)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:185)
org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:205)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:161)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:292)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:84)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:199)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:185)
org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:205)
org.jruby.internal.runtime.methods.WrapperMethod.call(WrapperMethod.java:59)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:161)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:292)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:78)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:164)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:150)
org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:197)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:129)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:308)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:66)
org.jruby.runtime.Block.yieldSpecific(Block.java:116)
org.jruby.RubyKernel.loop(RubyKernel.java:1292)
org.jruby.RubyKernel$INVOKER$s$0$0$loop.call(RubyKernel$INVOKER$s$0$0$loop.gen)
org.jruby.internal.runtime.methods.JavaMethod$JavaMethodZeroBlock.call(JavaMethod.java:473)
org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:273)
org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:79)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
org.jruby.ir.instructions.CallBase.interpret(CallBase.java:419)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:324)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:129)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:115)
org.jruby.RubyClass.finvoke(RubyClass.java:541)
org.jruby.RubyBasicObject.send19(RubyBasicObject.java:1631)
org.jruby.RubyBasicObject$INVOKER$i$send19.call(RubyBasicObject$INVOKER$i$send19.gen)
org.jruby.RubyKernel.public_send(RubyKernel.java:1824)
org.jruby.RubyKernel$INVOKER$s$0$0$public_send.call(RubyKernel$INVOKER$s$0$0$public_send.gen)
org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:77)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
org.jruby.ir.instructions.CallBase.interpret(CallBase.java:419)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:324)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:129)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:115)
org.jruby.ir.runtime.IRRuntimeHelpers.instanceSuper(IRRuntimeHelpers.java:890)
org.jruby.ir.instructions.InstanceSuperInstr.interpret(InstanceSuperInstr.java:69)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:324)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:84)
org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:199)
org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:185)
org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:205)
org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:313)
org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:163)
org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:292)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:78)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:66)
org.jruby.runtime.Block.yieldSpecific(Block.java:116)
org.jruby.ir.runtime.IRRuntimeHelpers.yieldSpecific(IRRuntimeHelpers.java:456)
org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:72)
org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:184)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:108)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:66)
org.jruby.runtime.Block.yieldSpecific(Block.java:116)
org.jruby.ir.runtime.IRRuntimeHelpers.yieldSpecific(IRRuntimeHelpers.java:456)
org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:72)
org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:184)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:108)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:66)
org.jruby.runtime.Block.yieldSpecific(Block.java:116)
org.jruby.ir.runtime.IRRuntimeHelpers.yieldSpecific(IRRuntimeHelpers.java:456)
org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:72)
org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:184)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:108)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:66)
org.jruby.runtime.Block.yieldSpecific(Block.java:116)
org.jruby.ir.runtime.IRRuntimeHelpers.yieldSpecific(IRRuntimeHelpers.java:456)
org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:72)
org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:184)
org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:108)
org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:137)
org.jruby.runtime.InterpretedIRBlockBody.commonYieldPath(InterpretedIRBlockBody.java:116)
org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:146)
org.jruby.runtime.BlockBody.yield(BlockBody.java:83)
org.jruby.runtime.Block.yieldArray(Block.java:161)
org.jruby.ext.fiber.ThreadFiber$1.run(ThreadFiber.java:259)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
java.lang.Thread.run(Thread.java:745)
When I try to start a classic mode app I get the following:
$ bundle exec ruby application.rb
I, [2017-09-20T09:53:15.848154 #20898] INFO -- : Celluloid 0.17.3 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
/usr/lib/ruby/2.3.0/forwardable.rb:182:in `def_instance_delegator': undefined method `method_defined?' for main:Object (NoMethodError)
Did you mean? method_missing
from /usr/lib/ruby/2.3.0/forwardable.rb:157:in `block in def_instance_delegators'
from /usr/lib/ruby/2.3.0/forwardable.rb:156:in `each'
from /usr/lib/ruby/2.3.0/forwardable.rb:156:in `def_instance_delegators'
from /var/lib/gems/2.3.0/gems/angelo-0.5.0/lib/angelo/main.rb:37:in `<top (required)>'
from application.rb:1:in `require'
from application.rb:1:in `<main>'
$ cat application.rb
require 'angelo/main'
get "/foo" do
"Hello world"
end
I am using angelo 0.5.0 and starting it with bundle exec ruby application.rb
.
If I convert to the class based solution everything works fine.
I believe I have found a bug. In angelo/base.rb
, line 287, you have:
warn e.message if report_errors?
self
at this point is #<Celluloid::Proxy::Cell(Angelo::Server:0x2b23a590ec0c) @base=MyHTTPServer @options={:host=>"0.0.0.0", :port=>2347} @callback=#<Proc:0x000056474bd20df0 (lambda)> @server=#<Celluloid::IO::TCPServer:0x000056474bcffdf8 @socket=#<TCPServer:fd 15, AF_INET, 0.0.0.0, 2347>>>
, which is also an instance of Angelo::Server
It does not have a method report_errors?
. However, @base
does, given that MyHTTPServer
is a subclass of Angelo::Base
.
I believe the line should be rewritten to:
warn e.message if @base.report_errors?
Thanks for fixing the before block exception bug. It now works with eventsource except that it doesn't have the special handling for getting a status other than 500 from the exception like the handle_request method does. I know you just put a new release out but I can easily work around this so don't feel the need to rush another one out.
I was working on adding support for handling arbitrary content types in responses and wanted to run my idea by you to see if you'd want it included in Angelo.
As far as I can tell only content types explicitly handled here and here are allowed in request handlers.
I'd like to a method to Angelo::Base
that allows you to declare new content types in the body of the App class like add_content_type(type_alias, mime_type, klass=nil, &block)
. It would let you declare a symbol alias to use in request handlers, the associated mime type for the response header, and a lambda or class (with a .call(body)
instance method) to process the response body.
For example if json handling wasn't built in it could be declared like this
class App < Angelo::Base
add_content_type :json, "application/json" do |body|
case body
when String
JSON.parse(body) # for the raises
body
when Hash
body.to_json
end
end
get '/' do
content_type :json
{ "this" => "becomes json" }
end
end
or with a class
class JSONResponseBody
def call(body)
case body
when String
JSON.parse(body) # for the raises
body
when Hash
body.to_json
end
end
end
class App < Angelo::Base
add_content_type :json, "application/json", JSONResponseBody
get '/' do
content_type :json
{ "this" => "becomes json" }
end
end
Not a big deal here, just thought I'd mention it. If you request a URL like /foo?bar
then a Angelo::FormEncodingError exception is raised due to the lack of = even though this is a valid URL. I would expect bar
to be present in the params hash as an empty string.
looks like this is work in progress - anything we can do to assist?
@kenichi Great work here BTW, love this framework
Hi. I'm not sure if I'm using this right, but shouldn't these be handled asyncrhonously?
require 'angelo'
class HelloApp < Angelo::Base
task :do_stuff do
time_of_request = Time.now
Kernel.sleep 10
time_after = Time.now
"Hello to you, too. #{time_of_request}- #{time_after}"
end
get "/" do
puts "processing"
f = future :do_stuff
f.value
end
end
HelloApp.run!
When I hit it, they are usually serviced 10 sec apart. Occasionally one gets serviced w/in 3 sec.
Perhaps I did not use future correctly? Thank you
websocket '/ws/' do |ws|
puts 'executed'
websockets << ws
end
This code is executed twice every time a websocket connects to the server.
Angelo 0.2.2 is the first affected version with this commit in server.rb: line 61 explicitly calls handle_request
, but Websocket#request=, line 15 already executes handle_request
.
The Content-Type header should always include a charset, at least for html, to prevent exploits. Angelo needs a way to at least specify a charset, and it would be wise to provide a default charset where appropriate for vulnerable Content-Types like text/html.
This happened a couple of times:
ERROR -- : Actor crashed!
ArgumentError: this IO is already registered with selector
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/reactor.rb:42:in `register'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/reactor.rb:42:in `wait'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/reactor.rb:26:in `wait_writable'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io.rb:65:in `wait_writable'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:33:in `wait_writable'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:63:in `rescue in block in syswrite'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:60:in `block in syswrite'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:389:in `synchronize'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:58:in `syswrite'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:355:in `do_write'
/xxx/gems/celluloid-io-0.16.2/lib/celluloid/io/stream.rb:256:in `<<'
/xxx/gems/reel-0.5.0/lib/reel/websocket.rb:80:in `write'
/yyy/angelo_apis.rb:32:in `block (2 levels) in <class:AngeloApis>'
/xxx/gems/angelo-0.4.1/lib/angelo/stash.rb:56:in `block in each'
/xxx/gems/angelo-0.4.1/lib/angelo/stash.rb:54:in `each'
/xxx/gems/angelo-0.4.1/lib/angelo/stash.rb:54:in `each'
/yyy/angelo_apis.rb:32:in `block in <class:AngeloApis>'
/xxx/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `public_send'
/xxx/gems/celluloid-0.16.0/lib/celluloid/calls.rb:26:in `dispatch'
/xxx/gems/celluloid-0.16.0/lib/celluloid/calls.rb:122:in `dispatch'
/xxx/gems/celluloid-0.16.0/lib/celluloid/cell.rb:60:in `block in invoke'
/xxx/gems/celluloid-0.16.0/lib/celluloid/cell.rb:71:in `block in task'
/xxx/gems/celluloid-0.16.0/lib/celluloid/actor.rb:357:in `block in task'
/xxx/gems/celluloid-0.16.0/lib/celluloid/tasks.rb:57:in `block in initialize'
/xxx/gems/celluloid-0.16.0/lib/celluloid/tasks/task_fiber.rb:15:in `block in create'
calling this task:
task :custom_event do |event|
websockets.each { |ws| ws.write event.to_json }
end
I saw this error a couple of times recently.
Do you think it can be an Angelo issue or can it be a problem of one of its dependency?
I found this
How the hell do you exit the server programmatically? Any attempt to call @server.terminate
, Thread.terminate
or Kernel.exit
simply results in an error message - or worse, a complete hang of the process.
Can reproduce with:
curl http://localhost:4567/scripts/cvslog.cgi?file=\<SCRIPT\>window.alert\<\/SCRIPT\>
The exception & stack trace:
E, [2014-09-04T21:59:51.850459 #873] ERROR -- : Angelo::Server crashed!
URI::InvalidURIError: bad URI(is not URI?): /scripts/cvslog.cgi?file=<SCRIPT>window.alert</SCRIPT>
/home/xxxxx/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/uri/common.rb:176:in `split'
/home/xxxxx/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/uri/common.rb:211:in `parse'
/home/xxxxx/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/uri/common.rb:747:in `parse'
/home/xxxxx/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/uri/common.rb:1232:in `URI'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/reel-0.5.0/lib/reel/mixins.rb:48:in `uri'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/reel-0.5.0/lib/reel/mixins.rb:52:in `path'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/angelo-0.1.21/lib/angelo/server.rb:39:in `dispatch!'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/angelo-0.1.21/lib/angelo/server.rb:23:in `block in on_connection'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/reel-0.5.0/lib/reel/connection.rb:73:in `each_request'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/angelo-0.1.21/lib/angelo/server.rb:21:in `on_connection'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/reel-0.5.0/lib/reel/server.rb:56:in `call'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/reel-0.5.0/lib/reel/server.rb:56:in `handle_connection'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `public_send'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/calls.rb:25:in `dispatch'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/calls.rb:122:in `dispatch'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/actor.rb:322:in `block in handle_message'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/actor.rb:416:in `block in task'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/tasks.rb:55:in `block in initialize'
/home/xxxxx/.rvm/gems/ruby-2.1.1@app/gems/celluloid-0.15.2/lib/celluloid/tasks/task_fiber.rb:13:in `block in create'
W, [2014-09-04T21:59:51.850789 #873] WARN -- : Terminating task: type=:call, meta={:method_name=>:run}, status=:iowait
Can Celluloid::Reel supervisors be used to ensure fault tolerance? How would I implement this with Angelo?
A fresh installation:
$ rvm gemset empty
$ gem install http angelo
angelo.rb
:
require 'angelo'
class Server < Angelo::Base
get '/' do
"Hello world"
end
end
$ ruby angelo.rb
I, [2016-03-12T16:54:45.041077 #84532] INFO -- : Celluloid 0.17.3 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
I, [2016-03-12T16:54:45.334780 #84532] INFO -- : Angelo 0.4.1
I, [2016-03-12T16:54:45.334898 #84532] INFO -- : listening on 127.0.0.1:4567
E, [2016-03-12T16:54:52.654852 #84532] ERROR -- : Actor crashed!
NameError: uninitialized constant HTTP::Response::SYMBOL_TO_STATUS_CODE
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo.rb:97:in `log'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:68:in `route!'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:51:in `dispatch!'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:29:in `block in on_connection'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/reel-0.6.0/lib/reel/connection.rb:73:in `each_request'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:27:in `on_connection'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/reel-0.6.0/lib/reel/server.rb:50:in `call'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/reel-0.6.0/lib/reel/server.rb:50:in `handle_connection'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/calls.rb:28:in `public_send'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/calls.rb:28:in `dispatch'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/call/async.rb:7:in `dispatch'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/cell.rb:50:in `block in dispatch'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/cell.rb:76:in `block in task'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/actor.rb:339:in `block in task'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/task.rb:44:in `block in initialize'
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/celluloid-0.17.3/lib/celluloid/task/fibered.rb:14:in `block in create'
The following code:
require 'bundler'
Bundler.require :default
require 'angelo/tilt/erb'
require 'angelo/mustermann'
class Foo < Angelo::Base
include Angelo::Tilt::ERB
include Angelo::Mustermann
get '/' do
erb :index
end
end
Foo.run!
produces undefined method
render' for nil:NilClass`
in gems/angelo-0.3.1/lib/angelo/tilt/erb.rb self.class.tempalates
doesn't contain :index
, only:
self.class.templates.keys
=> [:"index.erb"]
Using reel 0.6.1 got
Actor crashed!
ArgumentError: Reel::Request::StateMachine can't change state from 'closed' to 'headers'
when handling Net::HTTP POST
Raised issue in reel celluloid/reel#229
Hi @kenichi, I was trying angelo with reel 0.6.0.pre1 and I get the following error:
NameError: uninitialized constant WebSocket::Message
/xxx/angelo-ef0d4892d865/lib/angelo/base.rb:274:in `block (3 levels) in <class:Base>'
/xxx//angelo-ef0d4892d865/lib/angelo/stash.rb:88:in `block in all_each'
/xxx/angelo-ef0d4892d865/lib/angelo/stash.rb:86:in `each'
/xxx/angelo-ef0d4892d865/lib/angelo/stash.rb:86:in `all_each'
/xxx/angelo-ef0d4892d865/lib/angelo/base.rb:273:in `block (2 levels) in <class:Base>'
the problem is here: the code is using a class from the old websocket parser, that has been removed with the websocket-driver integration.
Hi,
Thanks for the elegant work.
It seems that Angelo::Base.run()
uses Angelo::Server.new
to start a new server instance, so I cannot call Angelo::Server.supervise_as
manually like using other Celluloid actors.
Is there a recommended way to make a supervised server always available in Celluloid::Actor[]
?
If you pass an absolute directory to public_dir
, it will be concatenated to the end of __FILE__
, for something like this:
/Users/kyle/server/lib/Users/kyle/public
send_file
has a couple of shortcomings that can be easily addressed:
get '/:file' do
if public_folder_has(params[:file])
send_file params[:file]
else
send_file "my_secret_folder/#{params[:file]}", absolute: true
end
end
By the way, passing the 'absolute: true' option could be a nice way to implement it
no implicit conversion of nil into String
as body. We could handle it in better way for example with a 404 cod, an empty body and logging it.What do you think?
Hello in 2022! We're still using Angelo for one of our projects and got an error when trying to switch to Ruby 3. The specific error seems to be related to the mustermann gem which is locked to an old version (0.4), but I guess there could be others. Any chance to update the angelo dependencies?
When I run an angelo server, I get the warning
Celluloid 0.17.0 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
If I take the recommendation from the url and require celluloid/current' before requiring
angelo`, angelo throws the following error:
/Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:8:in `<class:Server>': uninitialized constant Celluloid::Logger (NameError)
from /Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:6:in `<module:Angelo>'
from /Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo/server.rb:4:in `<top (required)>'
from /Users/kyle/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /Users/kyle/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /Users/kyle/.rvm/gems/ruby-2.2.3/gems/angelo-0.4.1/lib/angelo.rb:132:in `<top (required)>'
from /Users/kyle/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:128:in `require'
from /Users/kyle/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:128:in `rescue in require'
from /Users/kyle/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:39:in `require'
from angelo.rb:2:in `<main>'
You have a bug in instantiate_template_from_string
, where opts
aren't being passed correctly to Tilt.new
. In addition, in lib/angelo/templates.rb
, line 61, the code render = ->{ template.render(self, locals) }
should respect the :scope
option, if set.
I need to serve static files from two different folders.
Here is my idea:
public_dir
accepts also an Array of paths, for example public_dir ['html/public', 'secret_folder']
html/public
it will be servedsecret_folder
it will be servedThe biggest issue is that the method public_dir
, when used as an accessor, has to return an array: to solve it I'd implement a couple of new methods called send_public_file(file_name)
and public_file?(file_name)
, that way it's unlikely that a user will use public_dir
directly in his/her code.
What do you think?
Since I need it I'm going to implement it immediately, if you think it is a valuable feature you will have a pull request in a couple of days
Hi, I'm using Angelo to build a real time UI for our system.
The application generates events in its event loop, my goal is to send them to Angelo to broadcast websockets.
I cannot find anywhere in documentation or code a way to do it in a simple way, at the moment I added a method at Angelo::Server
class Server
def custom_event(*args)
@base.custom_event(*args)
end
end
and defined in my Angelo app
def self.custom_event(*args)
# do something
end
and my main application pushes events to the server, but it seem to a workaround.
Is there an integrated method to do so? Otherwise I can try to make this code more clean and elegant and send a pull request 😉
Just want to say first that I love this little framework, I've used celluloid as library before and this just really makes sense.
I'm building a little messaging app and I have created a few components like sessions and authentication, the problem is when I extracted them into a library I had no way to set them up before and after each request.
Because there is no rack and therefore no middleware I couldn't think of a way to wrap a request apart from before and after. I had a look at adding filters like sinatra has but I couldn't get the context for running the filter blocks right.
Maybe there is another way?
Angelo::Base parses ARGV every time, even if it is embedded in another application and it raises and exception every time it founds a unknown parameter.
When I run my app (for example ruby myApp.rb -verbose
), Angelo raises an error since it does not recognizes the -verbose
param.
I suggest to remove completely the ARGV parsing, since it can generate unexpected bug when the main application uses the same args of Angelo or at least move them in an optional module (example: require angelo
for common application, require angelo-embedded
for embedding Angelo inside other apps)
Whoops, just saw the cascadia preso on confreaks.
And was wondering if you considered using @opal to make the claim above true 😺
I though it was fun because I happened to use almost the same tagline for kinda the same demo last year in a live coding session at codemotion. Maybe would be interesting to demo angelo+opal.
here's the repo anyway https://github.com/elia/codemotion
Feel free to just close the issue, was intended to be just a heads up.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.