POE::C...Bayeux(3pm)

πŸ“¦
version 0.04 Β· 2 versions
, in fedora 43

πŸ’– POE::Component::Server::Bayeux β€” bestie edition πŸ’–

okay so. this is a page from the User Contributed Perl Documentation collection, and it's all about POE::Component::Server::Bayeux. pull up a chair, grab a boba, we're doing this πŸ§‹βœ¨


🌸 NAME 🌸

POE::Component::Server::Bayeux β€” a Bayeux/cometd server implementation, but make it POE. periodt.


✨ SYNOPSIS ✨

here's the whole vibe in code form, no edits, no notes:

use POE qw(Component::Server::Bayeux);

# Create the server, listening on port 8080
my $server = POE::Component::Server::Bayeux->spawn(
    Port  => 8080,
    Alias => 'bayeux_server',
);

# Create a local client, a reply-bot
POE::Session->create(
    inline_states => {
        _start => sub {
            my ($kernel, $heap) = @_[KERNEL, HEAP];
            $kernel->alias_set('test_local_client');

            # Subscribe to /chat/demo, assigning a state for events
            $kernel->post('bayeux_server', 'subscribe', {
                channel => '/chat/demo',
                client_id => $heap->{client_id},
                args => {
                    state => 'subscribe_response',
                },
            });
        },
        subscribe_response => sub {
            my ($kernel, $heap, $message) = @_[KERNEL, HEAP, ARG0];

            # Don't auto-reply to my own messages
            return if $message->{clientId} eq $heap->{client_id};

            # Auto-reply to every message posted
            $kernel->post('bayeux_server', 'publish', {
                channel => $message->{channel},
                client_id => $heap->{client_id},
                data => {
                    user => 'Autobot',
                    chat => "I got your message, ".($message->{data}{user} || 'anon'),
                },
            });
        },
    },
    heap => {
        client_id => 'test_local_client',
    },
);

$poe_kernel->run();

πŸ¦‹ DESCRIPTION πŸ¦‹

so this module implements the Bayeux Protocol (1.0draft1) from the Dojo Foundation. it also gets called cometd, and it's basically a low-latency routing protocol for JSON-encoded events flying between clients and servers in a publish-subscribe model. it's giving very much "everyone in the group chat gets the message at the same time" energy πŸ’…

this one right here is the server implementation. there's also a client hanging out at POE::Component::Client::Bayeux if that's what you need. with this server you can roll out a cometd server AND a basic HTTP server with POE communication capabilities. it even comes bundled with test code you can run in your browser to test out a basic chat program's functionality. iconic behavior, honestly.

⚠️ okay bestie, real talk: this is the first release of this code. not much testing has been done, so keep that in mind if you're planning to use this for production. it was developed for a production environment that's still being built, so future versions will be released over the next month that'll be more feature complete and less prone to errors. we're in our early era 🍼

πŸŽ€ USAGE πŸŽ€

πŸ’« spawn (...) πŸ’«

creates a new Bayeux server. arguments to this method:

  • Port (default: 80) β€” binds an HTTP server to this port.
  • Alias (default: 'bayeux') β€” the POE session alias that local clients post to.
  • AnonPublish (default: 0) β€” lets HTTP-connected clients publish without a handshake.
  • ConnectTimeout (default: 120) β€” how many seconds before an HTTP-connected client gets timed out and forced to rehandshake. clients must not go this long between having a connect open.
  • ClientMaxConnections (default: 10) β€” max number of concurrent connections allowed from a single IP address. heads up: this is only effective for the bayeux/cometd connections, because the simple HTTP server doesn't support counting concurrent connections.
  • Debug (default: 0) β€” either 0 or 1, indicates level of logging.
  • LogFile (default: undef) β€” if present, opens the file path indicated for logging output.
  • DocumentRoot (default: '/var/www') β€” document root of the generic HTTP server for file serving.
  • DirectoryIndex (default: [ 'index.html' ]) β€” index file (think Apache config).
  • TypeExpires (default: {}) β€” provide a hashref of MIME types and their associated expiry time. similar to mod_expires 'ExpiresByType $key "access plus $value seconds"'.
  • PostHandle (default: undef) β€” provide a subref which will be called with the HTTP::Request and HTTP::Response of any simple HTTP requests before the request is completed. this could allow the code to modify the headers of the response as needed (i.e., path-based expiry time).
  • Services (default: {}) β€” each key of this hash represents a service channel that'll be available. the name of the channel will be '/service/$key', and the handling is dependent on the $value. provide '_handler' as a fallback handler.

if $value is a coderef, the code will be called with a single arg of the message being acted upon. the return value(s) of the coderef will be considered response(s) to be sent back to the client, so return an empty array if you don't want this to happen (if you've added responses by $message->request->add_response()).

  • MessageACL (defaults: sub {}) β€” coderef to perform authorization checks on messages. the code block is passed two args, the Client and the Message. if the message should be rejected, the code should set is_error() on the message.

one could use this to perform authentication on the 'handshake' message:

sub {
    my ($client, $message) = @_;

    return unless $message->isa('POE::Component::Server::Bayeux::Message::Meta');
    return unless $message->type eq 'handshake';

    my $error;

    while (1) {
        if (! $message->ext ||
            ! (defined $message->ext->{username} && defined $message->ext->{password})) {
            $error = "Must pass username and password in ext to handshake";
            last;
        }

        my $authenticated = $message->ext->{username} eq 'admin'
            && $message->ext->{password} eq 'password' ? 1 : 0;

        if (! $authenticated) {
            $error = "Invalid username or password";
            last;
        }

        $client->flags->{is_authenticated} = 1;
        last;
    }

    if ($error) {
        $message->is_error($error);
    }
}
  • Callback (defaults: sub {}) β€” coderef to receive general event notifications from the server. sends a hashref like so:
{
    event => 'new_connection',
    client_id => ...,
    client => ...,
    message => ...,
}

see "Server Callbacks" for more details about every type of event that this will receive.

  • ContentHandler (defaults: {}) β€” additional ContentHandler for POE::Component::Server::HTTP creation. use this to extend the HTTP server content handling.

returns a class object with methods of interest:

  • logger β€” returns the Log::Log4perl object used by the server. use this for unified logging output. ✨
  • session β€” the POE::Session object returned from an internal create() call.

πŸ‘‘ POE STATES πŸ‘‘

most of the server code is regarding interaction with HTTP-connected clients. for that, go look at POE::Component::Server::Bayeux::Client. it supports locally connected POE sessions, and for that, it makes the following states available.

these same states are called internally to handle the basic PubSub behavior of the server for all clients, local and HTTP. equality πŸ’…

🌈 subscribe ({...}) 🌈

required keys 'channel', 'client_id'. optional key 'args' (hashref).

subscribes client_id to the channel indicated. if subscribe() is called by another session, it's treated as a non-HTTP request and will not perform authentication on the subscription. local clients need not handshake or connect. so chill of them fr fr.

events published to the subscribed channel are sent to the calling session's method named 'deliver', which can be overrided by the args hashref key 'state'. for example:

$kernel->post('bayeux_server', 'subscribe', {
    channel => '/chat/demo',
    client_id => 'local_client',
    args => {
        state => 'subscribe_events',
    },
});

πŸ’œ unsubscribe ({...}) πŸ’œ

required keys 'channel', 'client_id'.

unsubscribes client_id from the channel indicated. bye bestie πŸ‘‹

πŸ“ publish ({...}) πŸ“

required keys 'channel' and 'data'. optional keys 'client_id', 'id', and 'ext'.

publishes a message to the channel specified. the keys 'client_id', 'id' and 'ext' are passed thru, appended to the message sent. for local clients who subscribed from another session, the message is immediately posted to their callback state. for HTTP clients, messages are put into queue and flushed if they have an open /meta/connect.

🧚 client_push ({...}) 🧚

(no extra deets here in the original, she's just listed like that 😭)

⭐ Server Callbacks ⭐

using the Callback feature of the server spawning, you can be notified about every significant event on the server. below describes all the current callback events:

  • subscribe β€” keys 'client_id' and 'channel'
  • unsubscribe β€” keys 'client_id' and 'channel'
  • publish β€” keys 'channel' and 'data', optional: 'client_id', 'id', 'ext'
  • client_push β€” keys 'channel' and 'client_id', optional: (any extra). indicates data was pushed to the client not as a normal request/response or a publish/subscribe (out-of-sequence reply to a /service, for example). likely only triggered by local sessions.
  • client_connect β€” keys 'client_id' and either 'ip' or 'session' depending on the type of client.
  • client_disconnect β€” key 'client_id'.

πŸ’€ TODO πŸ’€

lots of stuff. very demure, very vague.

the code currently implements only the long-polling transport and doesn't yet strictly follow all the directives in the protocol document http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html


πŸ™ˆ KNOWN BUGS πŸ™ˆ

no known bugs, but I'm sure you can find some. challenge accepted, bestie 😭


πŸ”₯ SEE ALSO πŸ”₯

POE, POE::Component::Server::HTTP


Copyright (c) 2008 Eric Waters and XMission LLC (http://www.xmission.com/). all rights reserved. this program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

the full text of the license can be found in the LICENSE file included with this module.


πŸ’… AUTHOR πŸ’…

Eric Waters <ewaters@uarc.com>


okay that's the whole page, you ate that 🫢 this was perl v5.42.0, dated 2025-07-25, and yeah, that's the POE::C...Bayeux(3pm) page, abbreviated title and all. go build something cute with it βœ¨πŸŽ€

πŸ“¦ where this page comes from