# Pixie
[![Codeship](https://img.shields.io/codeship/eb1dde70-2d10-0133-c122-16954c8f6a18/master.svg)](https://codeship.com/projects/98754)
[![Hex.pm](https://img.shields.io/hexpm/v/espec.svg)](https://hex.pm/packages/pixie)
## Pixie is a [Faye](http://faye.jcoglan.com) compatible Bayeux implementation
Pixie is inspired by [Faye](http://faye.jcoglan.com/) and was originally
planned as a port, but has diverged significantly as I've learned the Erlang
way of modelling these sorts of problems.
# Heroku Add-on
If you're planning on running Faye on Heroku you're probably going to have a
bad time. Take a look at [MessageRocket](https://messagerocket.co/) as an
alternative, and help support the author to maintain more great open source
projects.
## License
Pixie is Copyright (c) 2015 James Harton and licensed under the terms of
the MIT Public License (see the LICENSE file included with this distribution
for more details).
## Installation
Add `pixie` to your dependencies in the `mix.exs` file:
```elixir
def deps do
# ...
{:pixie, "~> 0.1.3"}
# ...
end
```
Also to the `application` section of your `mix.exs` file:
```elixir
def application do
[
applications: [
# ...
:pixie,
# ...
]
]
end
```
Then use `mix deps.get` to download Pixie from [hex](https://hex.pm/).
## Status
Pixie is still pre 1.0, however it works and is compatible with the
popular [Faye](http://faye.jcoglan.com/) JavaScript and Ruby clients.
Pixie is used in production at [MessageRocket](https://messagerocket.co/) to
handle relatively large message loads. Development is mostly guided by the
needs of MessageRocket, however pull requests and issues are gratefully
received.
## Features
- Compatible with Faye JavaScript and Ruby clients.
- Supports both in-memory (ETS) and clustered (Redis) backends.
- Handles all Bayeux message types.
- Handles all Bayeux features except service channels.
- Handles the following connection types:
- long-polling
- cross-origin-long-polling
- callback-polling
- websocket
## Usage
Once you have pixie installed in your project you can run a stand alone-server
with `mix pixie.server`.
### Configuration
The configuration options and their defaults are shown here:
```elixir
# This is the default backend configuration, you don't need to set it.
config :pixie, :backend,
name: :ETS
# If you want to use Redis for clustering. The Redis backend defaults to
# localhost, unless you specify it here.
config :pixie, :backend,
name: :Redis,
redis_url: "redis://localhost:6379"
# When clients subscribe to channels we don't have to respond immediately, and
# can instead wait until there is a message to be sent on that channel or a
# heartbeat timeout expires, whichever happens first.
# Setting this option to true means that subscriptions are responded to
# which *may* increase time to first message for those not using websockets.
config :pixie, :subscribe_immediately, false
# Add extensions to be loaded at startup:
config :pixie, :extensions, [My.Extension.Module.Name]
# Add monitors to be loaded at startup:
config :pixie, :monitors, [
My.Monitor.Module.Name,
# ... or ...
{My.Monitor.Module.Name, [some_arg]}
]
# Explicitly configure transports available to clients:
config :pixie, :enabled_transports, ~w| long-polling cross-origin-long-polling callback-polling websocket |
```
### Using with Phoenix
You can add Pixie as a custom dispatcher rule for Phoenix with Cowboy by adding
the following to your application configuration:
```elixir
config :myapp, MyApp.Endpoint,
http: [
dispatch: [
{:_, [
{"/pixie", Pixie.Adapter.Cowboy.HttpHandler, {Pixie.Adapter.Plug, []}},
{:_, Plug.Adapters.Cowboy.Handler, {MyApp.Endpoint, []}}
]}
]
]
```
Obviously, you can change `"/pixie"` to any path you wish.
### Sending messages from the server
You can publish messages from within the server using `Pixie.publish`.
```elixir
Pixie.publish "/my/channel", %{message: "Pixie is awesome!"}
```
### Receiving messages from the server
You can subscribe to a channel and receive messages on that channel using
`Pixie.subscribe`.
```elixir
{:ok, pid} = Pixie.subscribe "/my/channel", fn (message, _pid)->
IO.puts "Received message: #{inspect message}"
end
```
A separate worker process is created for each subscription, and it's pid is
both returned from the `subscribe` call, but also passed as the second argument
into the callback function, which means that you can do things like receive a
single message, then unsubscribe:
```elixir
Pixie.subscribe "/only_one_message", fn(message, pid)->
IO.puts "Received message: #{inspect message}"
Pixie.unsubscribe pid
end
```
Either way, you can use `Pixie.unsubscribe pid` to unsubscribe and terminate
the subscription process.
### Writing extensions
Pixie supports extensions which allow you to modify messages as they come into
the server. You can write your own module and use the `Pixie.Extension`
behaviour. Your extension needs only implement two functions:
- `incoming %Pixie.Event{}` which returns a (possibly) modified event.
- `outgoing %Pixie.Message.Publish{}` which returns a (possibly) modified
message.
The `Pixie.Event` struct contains the following fields:
- `client_id`: The ID of the Client. You can use this to find `Pixie.Client`
and `Pixie.Transport` processes should you need to.
- `message`: The incoming message from the client. Messages are represented
as:
- `%Pixie.Message.Handshake{}`: A client handshake request.
- `%Pixie.Message.Connect{}`: A client connection request.
- `%Pixie.Message.Subscribe{}`: A subscription request.
- `%Pixie.Message.Publish{}`: A message to be published by the user.
- `%Pixie.Message.Unsubscribe{}`: An unsubscription request.
- `%Pixie.Message.Disconnect{}`: A client disconnection request.
- `response`: The response to be sent back to the client. You can use the
functions in `Pixie.Protocol.Error` (automatically imported
for you) or you can modify the response directly.
The details of all these structs should be available on
[hexdocs.pm](http://hexdocs.pm/pixie/overview.html).
You can configure Pixie to load your extensions at start-up (as per the
configuration section above) or you can add and remove them at runtime.
```elixir
Pixie.ExtensionRegistry.register MyExtension
# ... and ...
Pixie.ExtensionRegistry.unregister MyExtension
```
### Writing Monitors
Pixie provides monitoring functionality which allows you to subscribe to events
which are happennings in the system.
Provided events are:
- Client created.
- Client destroyed.
- Channel created.
- Channel destroyed.
- Client subscribed to channel.
- Client unsubscribed from channel.
- Message received for publication.
- Message delivered to receiving client.
All events also receive a [Timex](https://hex.pm/packages/timex) timestamp
recording the time at which they were generated - as there are potentially
a lot of them and they may sit in a process mailbox for some time.
You can use the `Pixie.Monitor` behaviour to define your event handler:
```elixir
defmodule MyMonitor do
use Pixie.Monitor
def created_channel channel_name, at do
Logger.info "Channel \#\{channel_name} created at \#\{format at}"
end
def destroyed_channel channel_name, at do
Logger.info "Channel \#\{channel_name} destroyed at \#\{format at}"
end
defp format timestamp do
timestamp
|> Date.from(:timestamp)
|> DateFormat.format!("{UNIX}")
end
end
```
Or you can use your own `GenEvent` handler, if the monitor API doesn't work
for you.
You can find more information in the [documentation](http://hexdocs.pm/pixie/overview.html).
## Running the tests
Run `mix espec`.
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request