{"id":13800563,"url":"https://github.com/ninenines/bullet","last_synced_at":"2025-05-13T09:31:45.542Z","repository":{"id":63979849,"uuid":"2470304","full_name":"ninenines/bullet","owner":"ninenines","description":"Simple, reliable, efficient streaming for Cowboy.","archived":true,"fork":false,"pushed_at":"2018-12-11T13:40:55.000Z","size":69,"stargazers_count":302,"open_issues_count":0,"forks_count":69,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-11-18T15:54:08.060Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://ninenines.eu","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ninenines.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-09-27T19:56:44.000Z","updated_at":"2024-06-27T04:30:57.000Z","dependencies_parsed_at":"2022-11-30T13:33:41.995Z","dependency_job_id":null,"html_url":"https://github.com/ninenines/bullet","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninenines%2Fbullet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninenines%2Fbullet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninenines%2Fbullet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ninenines%2Fbullet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ninenines","download_url":"https://codeload.github.com/ninenines/bullet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253913196,"owners_count":21983273,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T00:01:13.748Z","updated_at":"2025-05-13T09:31:44.683Z","avatar_url":"https://github.com/ninenines.png","language":"Erlang","funding_links":[],"categories":["Erlang","HTTP"],"sub_categories":[],"readme":"Bullet (discontinued)\n=====================\n\nThis project is discontinued and has been archived. It has not been\nupdated in a few years and I am no longer using it nor do I have\nthe time to maintain it. If you are using it and would like me to\npromote a fork or alternative based on Bullet, please send me an\nemail and I will oblige. Thanks for your interest!\n\nThe old README continues below.\n\nBullet is a Cowboy handler and associated Javascript library for\nmaintaining a persistent connection between a client and a server.\n\nBullet abstracts a general transport protocol similar to WebSockets.\nBullet will use a WebSocket if possible but will fallback to other\ntransports when necessary. If the client supports EventSource\nconnections then Bullet will use EventSource to send messages from the\nserver to the client and XHR for messages from the client to the\nserver. If EventSource is not available then Bullet will use XHR for\nboth directions, using long-polling for server-to-client messages.\n\nA common interface is defined for both client and server to easily\nfacilitate the handling of such connections. Bullet additionally takes care\nof reconnecting automatically whenever a connection is lost, and also\nprovides an optional heartbeat which is managed on the client side.\n\nDispatch options\n----------------\n\nSimilar to any other handler, you need to setup the dispatch list before\nyou can access your Bullet handlers. Bullet itself is a Cowboy HTTP\nhandler that translates some of the lower-level functions into a\nsimplified higher-level interface.\n\nThe dispatch options for a Bullet handler looks as follow:\n\n``` erlang\n{[\u003c\u003c\"path\"\u003e\u003e, \u003c\u003c\"to\"\u003e\u003e, \u003c\u003c\"bullet\"\u003e\u003e], bullet_handler,\n\t[{handler, my_stream}]}\n```\n\nSimply define this in your dispatch list and your handler will be\navailable and handled by Bullet properly.\n\nThe third element in the tuple ([{handler, my_stream}]) will be passed \nto init/4 as Opts, you can add your own options and get them using\nlists:keyfind, for example if we define our handler as:\n\n``` erlang\n{[\u003c\u003c\"path\"\u003e\u003e, \u003c\u003c\"to\"\u003e\u003e, \u003c\u003c\"bullet\"\u003e\u003e], bullet_handler,\n\t[{handler, my_stream}, {channel, \"my channel\"}]}\n```\n\nyou can retrieve the channel value as follows:\n\n``` erlang\ninit(_Transport, Req, Opts, _Active) -\u003e\n\t{channel, Channel} = lists:keyfind(channel, 1, Opts),\n\t{ok, Req, #state{channel=Channel}}.\n```\n\nCowboy handler\n--------------\n\nSimilar to websocket handlers, you need to define 4 functions.\nA very simple bullet handler would look like the following:\n\n``` erlang\n-module(stream_handler).\n-export([init/4, stream/3, info/3, terminate/2]).\n\ninit(_Transport, Req, _Opts, _Active) -\u003e\n\t{ok, Req, undefined_state}.\n\nstream(Data, Req, State) -\u003e\n\t{reply, Data, Req, State}.\n\ninfo(_Info, Req, State) -\u003e\n\t{ok, Req, State}.\n\nterminate(_Req, _State) -\u003e\n\tok.\n```\n\nOf note is that the init/4 and terminate/2 functions are called\neverytime a connection is made or closed, respectively, which can\nhappen many times over the course of a bullet connection's life,\nas Bullet will reconnect everytime it detects a disconnection.\n\nNote that you do not need to handle a heartbeat server-side, it\nis automatically done when needed by the Bullet client as explained\nlater in this document.\n\nYou might have noticed the odd Active argument to init/4. It\nindicates what type of connection we have. When Active == once,\nwe have a temporary connection that only allows one reply before\nterminating. When Active == true, the connection allows any number\nof replies. You can use this information to inform your session\nprocess that it should send only 1 message, in the case of\nActive == once, or that it can send messages whenever in the\ncase of Active == true.\n\nYou would typically use init/4 to inform your session process\nthat it can send you messages. In the same manner you can use\nterminate/2 to inform it that the connection is going down.\n\nBullet handlers should only contain transport related code,\nlogic should be done in your session process if any, or other\nparts of your application. Bullet processes should be considered\ntemporary as you never know when a connection is going to close\nand therefore lose your State.\n\nClient-side javascript\n----------------------\n\nBullet requires the jQuery library to be used. Initializing a\nbullet connection is quite simple and can be done directly from\na document.ready function like this:\n\n``` js\n$(document).ready(function(){\n\tvar bullet = $.bullet('ws://localhost/path/to/bullet/handler');\n\tbullet.onopen = function(){\n\t\tconsole.log('bullet: opened');\n\t};\n\tbullet.ondisconnect = function(){\n\t\tconsole.log('bullet: disconnected');\n\t};\n\tbullet.onclose = function(){\n\t\tconsole.log('bullet: closed');\n\t};\n\tbullet.onmessage = function(e){\n\t\talert(e.data);\n\t};\n\tbullet.onheartbeat = function(){\n\t\tbullet.send('ping');\n\t}\n});\n```\n\nUse the WebSocket (ws:) form for your bullet URLs and Bullet\nwill change the URL as needed for non-WebSocket transports.\n\nUse the standard (http:) form for your bullet URLs and Bullet\nwill only try non-WebSocket transports.\n\nThe `$.bullet` function takes an optional second 'options' object.\nThe following properties are supported:\n\n| Name                   | Default | Description                         |\n| ---------------------- | --------|------------------------------------ |\n| disableWebSocket       | false   | Never make WebSocket connections.   |\n| disableEventSource     | false   | Never make EventSource connections. |\n| disableXHRPolling      | false   | Never fallback to XHR long polling. |\n\nNote that if EventSource is enabled and chosen as the underlying\ntransport, XHR will be used for client-to-server messages.\n\nBullet works especially well when it is used to send JSON data\nformatted with the jQuery JSON plugin.\n\n``` js\nbullet.send($.toJSON({type: 'event', data: 'hats!'}));\n```\n\nWhen receiving JSON you would typically receive a list of events,\nin which case your onmessage handler can look like this, assuming\nyou previously defined a handlers function array for all your events:\n\n``` js\n\tbullet.onmessage = function(e){\n\t\tvar obj = $.parseJSON(e.data);\n\t\tfor (i = 0; i \u003c obj.length; i++){\n\t\t\thandlers[obj[i].type](obj[i]);\n\t\t}\n\t};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fninenines%2Fbullet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fninenines%2Fbullet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fninenines%2Fbullet/lists"}