Plack::Handler::Stomp - adapt STOMP to (almost) HTTP, via Plack


version 1.15


  my $runner = Plack::Handler::Stomp->new({
    servers => [ { hostname => 'localhost', port => 61613 } ],
    subscriptions => [
      { destination => '/queue/plack-handler-stomp-test' },
      { destination => '/topic/plack-handler-stomp-test',
        headers => {
            selector => q{custom_header = '1' or JMSType = 'test_foo'},
        path_info => '/topic/ch1', },
      { destination => '/topic/plack-handler-stomp-test',
        headers => {
            selector => q{custom_header = '2' or JMSType = 'test_bar'},
        path_info => '/topic/ch2', },


Sometimes you want to use your very nice web-application-framework dispatcher, module loading mechanisms, etc, but you're not really writing a web application, you're writing a ActiveMQ consumer. In those cases, this module is for you.

This module is inspired by Catalyst::Engine::Stomp, but aims to be usable by any PSGI application.

Roles Consumed

We consume Net::Stomp::MooseHelpers::CanConnect and Net::Stomp::MooseHelpers::CanSubscribe. Read those modules' documentation to see how to configure servers and subscriptions.



A logger object used by thes handler. Not to be confused by the logger used by the application (either internally, or via a Middleware). Can be any object that can trace, debug, info, warn, error. Defaults to an instance of Log::Any::Proxy. This logger is passed on to the Net::Stomp object held in connection (see Net::Stomp::MooseHelpers::CanConnect).


A hashref mapping destinations (queues, topics, subscription ids) to URI paths to send to the application. You should not modify this.


If true, exit after the first message is consumed. Useful for testing, defaults to false.



Given a PSGI application, loops forever:

If the application throws an exception, the loop exits re-throwing the exception. If the STOMP connection has problems, the loop is repeated with a different server (see next_server in Net::Stomp::MooseHelpers::CanConnect).

If "one_shot" is set, this function exits after having consumed exactly 1 frame.


Loop forever receiving frames from the STOMP connection. Call "handle_stomp_frame" for each frame.

If "one_shot" is set, this function exits after having consumed exactly 1 frame.


Delegates the handling to "handle_stomp_message", "handle_stomp_error", "handle_stomp_receipt", or throws Plack::Handler::Stomp::Exceptions::UnknownFrame if the frame is of some other kind. If you want to handle different kind of frames (maybe because you have some non-standard STOMP server), you can just subclass and add methods; for example, to handle STRANGE frames, add a handle_stomp_strange method.


Logs the error via the "logger", level warn.


Calls "build_psgi_env" to convert the STOMP message into a PSGI environment.

The environment is then passed to "process_the_message", and the frame is acknowledged.


Runs a PSGI environment through the application, then flattens the response body into a simple string.

The response so flattened is sent back via "maybe_send_reply".


Logs (level debug) the receipt id. Nothing else is done with receipts.


Calls "where_should_send_reply" to determine if to send a reply, and where. If it returns a true value, "send_reply" is called to actually send the reply.


Returns the header X-Reply-Address or X-STOMP-Reply-Address from the response.


Converts the PSGI response into a STOMP frame, by removing the prefix x-stomp- from the key of header fields that have it, removing entirely header fields that don't, and stringifying the body.

Then sends the frame.


after modifier on the method provided by Net::Stomp::MooseHelpers::CanSubscribe.

It sets the "destination_path_map" to map the destination and the subscription id to the path_info slot of the "subscriptions" element, or to the destination itself if path_info is not defined.


Builds a PSGI environment from the message, like:

  # server
  SERVER_NAME => 'localhost',

  # client
  REQUEST_URI => $path_info,
  SCRIPT_NAME => '',
  PATH_INFO => $path_info,

  # broker
  REMOTE_ADDR => $server_hostname,

  # http
  HTTP_USER_AGENT => 'Net::Stomp',
  CONTENT_LENGTH => length($body),
  CONTENT_TYPE => $content-type,

  # psgi
  'psgi.version' => [1,0],
  'psgi.url_scheme' => 'http',
  'psgi.multithread' => 0,
  'psgi.multiprocess' => 0,
  'psgi.run_once' => 0,
  'psgi.nonblocking' => 0,
  'psgi.streaming' => 1,

In addition, reading from psgi.input will return the message body, and writing to psgi.errors will log via the "logger" at level error.

Finally, every header in the STOMP message will be available in the "namespace" jms., so for example the message type is in jms.type.

The $path_info is obtained from the "destination_path_map" (i.e. from the path_info subscription options) passed through munge_path_info.


You can find examples of use in the tests, or at


Gianni Ceccarelli <>


This software is copyright (c) 2020 by

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.