ninenines / bullet Goto Github PK
View Code? Open in Web Editor NEWSimple, reliable, efficient streaming for Cowboy.
Home Page: http://ninenines.eu
License: ISC License
Simple, reliable, efficient streaming for Cowboy.
Home Page: http://ninenines.eu
License: ISC License
Any plans to support fallbacks fro Websocket binary frames?
I got bullet working nicely across IE,Firefox and Chrome. The library seems to give up on browsers running on mobile devices, I tried Safari (tested on new IOS on an ipad) Browser (Mozilla) and Chrome running on an android phone. Neither of the transports works, no matter if it is over ssl or not.
As you may have guessed I want to have secure full duplex connection with a long polling and the like fallback if websockets are not available. I thought bullet would be the best for that as n2o uses it.
Can anyone advise whether
I looked at sockjs but bullet seems much cleaner, no parse transforms and iframe tricks on the client side?
BTW: very good job! and sorry if this is not the place to post comments like that, if not can you direct me to the relevant place?
Bullet master still depends on cowboy 0.8.0, but while trying to upgrade, I've seen unexpected flooding only in long-polling mode.
With using the simple bullet/examples/clock app I get the following
the current working behaviour of long-polling:
~/git/bullet/examples/clock/deps/cowboy (46cce48) "Update to 0.8.0"
~/git/bullet/examples/clock (master)
$ start.sh
Eshell V5.10.1 (abort with ^G)
1> Point your browser at http://localhost:8080
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:50 GMT">> <0.157.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:52 GMT">> <0.158.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:53 GMT">> <0.159.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:54 GMT">> <0.160.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:55 GMT">> <0.161.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:57 GMT">> <0.162.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:58 GMT">> <0.163.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:34:59 GMT">> <0.164.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:35:00 GMT">> <0.165.0>
1> bullet terminate
1>
then testing with cowboy master, or just after commit 3ea8551:
the handler gives a first reply as expected, after it starts fllooding, with more than 1 response
~/git/bullet/examples/clock/deps/cowboy (3ea8551) "Make sure socket is passive once we've done with loop handler"
~/git/bullet/examples/clock (master)
$ start.sh
Eshell V5.10.1 (abort with ^G)
1> Point your browser at http://localhost:8080
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:46 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:47 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:47 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:48 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:48 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:48 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:48 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:49 GMT">> <0.156.0>
1> bullet terminate
1> bullet init
1> clock refresh timeout: <<"Sun, 21 Apr 2013 22:40:50 GMT">> <0.156.0>
1> bullet terminate
1>
Adroit Solutions github
In testing long polling locally in an implementation that uses Bullet, I noticed I received server errors in bullet_handler whenever the client attempted to send data. In tracing it down, it appears that line 80 in bullet_handler,
handle(Req, State=#state{handler=Handler, handler_state=HandlerState},
'POST')
should instead be
handle(Req, State=#state{handler=Handler, handler_state=HandlerState},
<<"POST">>)
I try start this example on heroku and when i chek WebSocket i have this error:
=ERROR REPORT==== 13-Dec-2013::06:58:43 ===
Error in process <0.1286.0> with exit value: {[{reason,function_clause},{mfa,{bullet_handler,init,3}},{stacktrace,[
{bullet_handler,get_mode,[undefined,{http_req,#Port<0.2750>,ranch_tcp,close,<0.1286.0>,<<3 bytes>>,'HTTP/1.1',
{{10,238,165,77},39733},<<22 bytes>>,undefined,80,<<7 bytes>>,undefined,<<0 bytes>>,undefined,[],[{<<15 bytes>>,
<<13 bytes>>},{<<17 bytes>>,<<4 bytes>>},{<<15 bytes>>,<<25 bytes>>},{<<3 bytes>>,<<28 bytes>>},{<<10 bytes>>,
<<101 bytes>>},{<<21 bytes>>,...
=ERROR REPORT==== 13-Dec-2013::06:58:43 ===
Ranch listener http had connection process started with cowboy_protocol:start_link/4 at <0.1286.0> exit with reason
: {[{reason,function_clause},{mfa,{bullet_handler,init,3}},{stacktrace,[{bullet_handler,get_mode,[undefined,
{http_req,#Port<0.2750>,ranch_tcp,close,<0.1286.0>,<<"GET">>,'HTTP/1.1',{{10,238,165,77},39733},
<<"uucs1.herokuapp.com">>,undefined,80,<<"/bullet">>,undefined,<<>>,undefined,[],[{<<"x-request-start">>,<<"13869
17923765">>},{<<"x-forwarded-proto">>,<<"http">>},{<<"x-forwarded-for">>,<<"197.8.155.61, 192.168.0.99">>},
{<<"via">>,<<"1.1 wall.local (squid/3.3.9)">>},{<<"user-agent">>,<<"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/31.0.1650.63 Safari/537.36">>},{<<"sec-websocket-version">>,<<"13">>},{<<"sec-websocket-key">>,
<<"AlfdfQ9Z08d4WncgR4qQsw==">>},{<<"sec-websocket-extensions">>,<<"x-webkit-deflate-frame">>},{<<"pragma">>,
<<"no-cache">>},{<<"origin">>,<<"http://uucs1.herokuapp.com">>},{<<"host">>,<<"uucs1.herokuapp.com">>}
,{<<"connection">>,<<"close">>},{<<"cache-control">>,<<"no-cache">>}],[{<<"accept">>,undefined},{<<"connection">>,
[<<"close">>]}],undefined,[],waiting,undefined,<<>>,false,waiting,[],<<>>,undefined}],[{file,"src/bullet_handler.erl"},{line,175}]},
{bullet_handler,init,4,[{file,"src/bullet_handler.erl"},{line,57}]},{cowboy_handler,handler_init,4,[{file,"src/cowboy_handler.erl"},{line
,69}]},{cowboy_protocol,execute,4,[{file,"src/cowboy_protocol.erl"},{line,529}]}]},{req,[{socket,#Port<0.2750>},{transport,ranch_tcp},
{connection,close},{pid,<0.1286.0>},{method,<<"GET">>},{version,'HTTP/1.1'},{peer,{{10,238,165,77},39733}},{host,<<"uucs1.
herokuapp.com">>},{host_info,undefined},{port,80},{path,<<"/bullet">>},{path_info,undefined},{qs,<<>>},{qs_vals,undefined},
{bindings,[]},{headers,[{<<"x-request-start">>,<<"1386917923765">>},{<<"x-forwarded-proto">>,<<"http">>},
{<<"x-forwarded-for">>,<<"197.8.155.61, 192.168.0.99">>},{<<"via">>,<<"1.1wall.local (squid/3.3.9)">>},{<<"user-agent">>,
<<"Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36">>},
{<<"sec-websocket-version">>,<<"13">>},{<<"sec-websocket-key">>,<<"AlfdfQ9Z08d4WncgR4qQsw==">>},
{<<"sec-websocket-extensions">>,<<"x-webkit-deflate-frame">>},{<<"pragma">>,<<"no-cache">>},{<<"origin">>,
<<"http://uucs1.herokuapp.com">>},{<<"host">>,<<"uucs1.herokuapp.com">>},{<<"connection">>,<<"close">>},
{<<"cache-control">>,<<"no-cache">>}]},{p_headers,[{<<"connection">>,[<<"close">>]}]},{cookies,undefined},{meta,[]},
{body_state,waiting},{multipart,undefined},{buffer,<<>>},{resp_compress,false},{resp_state,waiting},{resp_headers,[]},
{resp_body,<<>>},{onresponse,undefined}]},{opts,[{handler,stream_handler}]}],[{cowboy_protocol,execute,4,
[{file,"src/cowboy_protocol.erl"},{line,529}]}]}
Is this the default bullet behaviour? Just checking, never had a chance to test with a separate bullet project.
when Handler:info is called and the message received requires to shutdown the connection it can be done on COMET (reply some empty thing), on websocket return shutdown https://github.com/ninenines/bullet/blob/master/src/bullet_handler.erl#L136 but not with eventsource (sse).
what I propose is to allow eventsource to reply shutdown and add a new case clause here https://github.com/ninenines/bullet/blob/master/src/bullet_handler.erl#L107 with
{shutdown, Req2, HandlerState2} when GetMode == eventsource -> {ok, Req2, State2}
it could also handle the case where GetMode == poll by replying 204.
will you accept the pull request? if so, only for eventsource or for poll too?
do you recommend an alternative way?
The connection can be closed from the 'init' callback by replying with 'shutdown', but this option is not available for the 'stream' and 'info' callbacks. There does not seem to be any way to do it from those callbacks.
As far as I can tell, once a connection is opened, after 'init' returns, there is no way for the server to close that connection. It stays open forever until closed by the client. Unless there is some indirect method based on the connection's PID or by altering the callback's returned Req value, but I was not able to find a way using either of those methods.
right now it crashes on a bad match and my logs have a lot of entries like this:
2015-07-13 11:54:01.305 [error] emulator Error in process <0.16957.914> on node '[email protected]' with exit value: {[{reason,{badmatch,{error,closed}}},{mfa,{bullet_handler,info,3}},{stacktrace,[{bullet_handler,reply_get_mode,3,[{file,"src/bullet_handler.erl"},{line,201}]},{bullet_handler,info,3,[{file,"src/bullet_handler.erl"},{line...
I would do the change, it's matching against close here: https://github.com/extend/bullet/blob/master/src/bullet_handler.erl#L201
the thing is that I don't know what to do when I get a close, maybe add a new return value and match it here: https://github.com/extend/bullet/blob/master/src/bullet_handler.erl#L112 and then stop?
Compile and run the clock example from Ubuntu and I get.
The plain cowboy samples work (and a lot of others).
mattias@ubuntu:~/erl-src/erlang/bullet/examples/clock$ rebar get-deps compile
==> cowlib (get-deps)
==> ranch (get-deps)
==> cowboy (get-deps)
==> bullet (get-deps)
==> clock (get-deps)
==> cowlib (compile)
==> ranch (compile)
==> cowboy (compile)
==> bullet (compile)
==> clock (compile)
mattias@ubuntu:~/erl-src/erlang/bullet/examples/clock$ ./start.sh
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.4 (abort with ^G)
1> {"init terminating in do_boot",{{badmatch,{error,{bad_return,{{clock_app,start,[normal,[]]},{'EXIT',{{badmatch,{error,{{shutdown,{failed_to_start_child,ranch_acceptors_sup,{{badmatch,{error,eaddrinuse}},[{ranch_acceptors_sup,init,1,[{file,"src/ranch_acceptors_sup.erl"},{line,38}]},{supervisor,init,1,[{file,"supervisor.erl"},{line,239}]},{gen_server,init_it,6,[{file,"gen_server.erl"},{line,304}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}}},{child,undefined,{ranch_listener_sup,http},{ranch_listener_sup,start_link,[http,100,ranch_tcp,[{port,8080}],cowboy_protocol,[{env,[{dispatch,[{'_',[],[{[],[],toppage_handler,[]},{[<<6 bytes>>],[],bullet_handler,[{handler,stream_handler}]},{[<<6 bytes>>,'...'],[],cowboy_static,{priv_dir,bullet,[]}}]}]}]}]]},permanent,5000,supervisor,[ranch_listener_sup]}}}},[{clock_app,start,2,[{file,"src/clock_app.erl"},{line,21}]},{application_master,start_it_old,4,[{file,"application_master.erl"},{line,269}]}]}}}}}},[{clock,start,0,[{file,"src/clock.erl"},{line,27}]},{init,start_it,1,[]},{init,start_em,1,[]}]}}
Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
mattias@ubuntu:~/erl-src/erlang/bullet/examples/clock$
The clock example is not working as is. Raising issue as per suggestion from essen in irc/erlang.
The issue is in the line lines below
{"/static/[...]", cowboy_static, [
{directory, {priv_dir, bullet, []}},
{mimetypes, [
{<<".js">>, [<<"application/javascript">>]}
]}
]}
Currently Bullet offers no cross origin support for the long polling fallback. I'm not sure it makes sense to add this (all browsers other than IE have supported websockets for the past 9 months, and IE 10 supports it natively so it's only a matter of time before it is made obsolete), but adding it would not be hard, and it seems like it could be an interesting WTF for a developer in the mean time if they were lackadaisical in testing the long polling fallback in a cross domain context.
Adding it would not be difficult, however, to what extent user participation should be required would need to be decided upon.
Essentially, the bullet_handler would need to accept an "OPTIONS" request, that returns a response with the header "Access-Control-Allow-Origin", and the domain(s) to allow as the value, as well as a "Access-Control-Allow-Headers" with the headers the response needs as declared in the request header "Access-Control-Request-Headers" (by default; it might also make sense to allow the user to configure this should they need to). The "Access-Control-Allow-Origin" header would need to be supplied for the POST as well.
I'd not be averse to taking a stab at implementing this on a fork, but it begs the question, A. What should the interface look like, and B. Should restricting the allowed domains also be included as part of the websocket connection? I.e., the handler to supply to Bullet has an optional function to declare the allowed domains, otherwise it defaults to all. Then use that for the CORS header value in case of long polling, as well as check against it before allowing a websocket connection to be opened.
I am getting:
WebSocket connection to 'ws://xxx.xxx.xxxx.xxxx:8000/path' failed: Error in connection establishment: net::ERR_TUNNEL_CONNECTION_FAILED
on line 223:
var ret = new t.transport(url);
Would be nice if bullet detected the error and fell back onto xhr...
Would be nice if bullet attempted spdy first before falling back onto ws and http. If not possible please advise what would have to be changed in Cowboy first.
I'm conscious it's a bit of a newbie question but what would be the best way to achieve this :
Let's imagine that each user connected has its own process, what's the best way to link one ( or multiple bullet handlers depending on the opened tabs / browser of the user ) bullet handler to the user process ?
When using https://raw.github.com/extend/bullet/master/src/bullet_handler.erl
I get an error of:
src/bullet_handler.erl:5: cannot parse file, giving up
src/bullet_handler.erl:5: cannot translate from UTF-8
(I reordered the lines so that it looked like:
-module(bullet_handler).
-behaviour(cowboy_http_handler).
-behaviour(cowboy_websocket_handler).
%% Copyright (c) 2011-2012, Loic Hoguin [email protected]
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
)
Anyway, the problem is that erlang (r17-rc2 on Ubuntu 64) seems to
dislike the " ï " in Loic, and changing it to " i " fixed it.
IE need to get Content type: text/html
Otherway IE said: "XHR null or (something)"
How to set Content type in Cowboy?
the most welcome imho are acknowledgements to send(), basic queues and message expirations
In Chrome/Safari - after 60 seconds without message - websocket will be closed.
It's bug, or feature? Or without "ping-pong" - no chance to have undead websocket connect?
hi guys,
I tested clock example on my android tablet with the stock browser and it doesn't work. It says "bullet not started". Isn't bullet supposed to work on browsers that don't support websockets?
When we have dependency on bullet, the below error is shown after rebar downloads dependencies and compiles. The error goes away after adding the mentioned latin-1 inline as the first line. The file should basically be with UTF-8 encoding or the mentioned latin-1 line should be added in repository so that this error need not be fixed by every individual after reps are downloaded.
src/bullet_handler.erl:1: Non-UTF-8 character(s) detected, but no encoding declared. Encode the file in UTF-8 or add "%% coding: latin-1" at the beginning of the file. Retrying with latin-1 encoding.
(As for me) must happened once.
Now - this event happened every time after unsuccessful connection.
I just add boolean variable for check status - (if we lost connection - we run ondisconnect. If we had "no connection" and lost connect - it's not "OnDisconnect")
(Sorry for my english)
Why not a bullet_sse_handler, with a fine fallback transport just as the bullet_websocket_handler ?
How to make reconnect on easy way?
When I made connect, then off/on server - I didn't saw onerror handler. Just error. And no auto reconnect on this way.
Error handler doesn't working?
Better solution - restart bullet when I don't take "ping-pong timestamp" from server more then N seconds?
My WebSocketon web page based on javascript and Bullet.js work fine! thank you @essen
But i have any problem...
How i may debugging WebSockets server based on Bullet with CURL
how to send a command to test server WebSocket bullet?
I try this:
curl -i -N -H "Connection: Upgrade" \
-H "Upgrade: websocket" \
http://websocket1.herokuapp.com/bullet/
but get in return this:
HTTP/1.1 400 Bad Request
Server: Cowboy
Date: Sun, 05 Jan 2014 11:41:10 GMT
Content-Length: 0
Connection: close
I'm new to bullet, the demo is impressive.
However, when I try to plug in bullet into my project, I realise it is depending on cowboy 1, and erlang vm doesn't allow nested dependency like nodejs do (only flat dependecies). So, to gain the performance improvement from cowboy 2, I've to not use bullet for now.
Hello,
I'm trying to implement communication between server and client using bullet but i have the following error when i call bullet.send('some-text') :
Error: InvalidStateError: DOM Exception 11
Error: An attempt was made to use an object that is not, or is no longer, usable.
This exception is thrown by the line 253 in bullet.js
this.send = function(data){
return transport.send(data);
};
I can use bullet.send in bullet.onopen only. Is it normal ? Should we use ajax requests to send to the server at any time ?
Thank you
Using erlang.mk in my project with Cowboy and Bullet as dependencies.
ERLC bullet_handler.erl
compile: warnings being treated as errors
src/bullet_handler.erl:16: behaviour cowboy_http_handler undefined
src/bullet_handler.erl:17: behaviour cowboy_websocket_handler undefined
make[1]: *** [ebin/bullet.app] Error 1
In toppage_handler.erl there is a row
bullet = $.bullet('ws://localhost:8080/bullet', options);
So, if you want to try with other clients on other computers, you will just get "offline".
Confuses newbies like me. Nice to be able to compare difference browers, like IE11, which doesn't support "eventsource only".
We got an error in FireFox when put bullet after nginx:
Firefox can't establish a connection to the server at ws://demo/pull/connect/.
Then a lots of:
Firefox can't establish a connection to the server at ws://demo/pull/connect/.
var ret = new t.transport(url);
(Like 20th attempt).
Then, we've got a same XHR instance works in "parallels mode"
Sounds like troubles with switch transport to XHR, because nginx doesn't support websockets yet.
Loïc. Would You be so kindly to add some example of usage for this code?
How should handler properly behave on once/false flags, etc?
We've got lots of errors:
Unexpected response code: 400
after Chrome update. Yes, WebSocket transport is off on our server side, and I think the problem is in switch to XHR.
I think, you forgot to add this:
in "success" key in post (XHR)
if (fake.readyState == OPEN){
nextPoll();
}
Without this "if" we send data (by POST) and lost connect.
Sorry - don't find how to send Pull Request to you ;)
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.