Contact Us 1-800-596-4880

HTTP Listener Reference - Mule 4

The HTTP listener is an event source that enables you to set up an HTTP server and trigger flows when HTTP requests are received.

You can choose what methods the source accepts, such as GET, POST or a list of methods, and on which path to accept requests, thereby allowing the routing of requests through different flows.

Once a request is accepted by the listener, the corresponding flow is triggered with the HTTP body as payload and the HTTP data as attributes (headers, query parameters and so on).

When the flow finishes its execution, the HTTP listener enables you to customize the HTTP response based on whether the execution was successful or not, so that different status codes can be returned, for example.

HTTP Listener Configuration

To use an HTTP listener, you need to declare a configuration with a corresponding connection. This declaration establishes the HTTP server that will listen to requests.

Additionally, you can configure a base path that applies to all listeners using the configuration.

<http:listener-config name="HTTP_Listener_config" basePath="api/v1">
  <http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>

HTTP Listener Connection

The connection defines the host and port where the server will be set up, as well as the protocol to use: HTTP for plain connections or HTTPS for TLS connections.

To allow secure connections through HTTPS, you need to define a TLS context in the connection and provide a key store for the server. You can also provide a trust store if you need two-way authentication. See TLS Configuration for details.

<http:listener-connection host="0.0.0.0" port="8081" protocol="HTTPS">
  <tls:context>
    <tls:trust-store path="keystore.jks" password="MyP455W0rD"/>
  </tls:context>
</http:listener-connection>
=== To deploy a project to CloudHub, setting the host to 0.0.0.0 is required because it sets up the connection to listen on all interfaces of the machine.

When you deploy locally for testing purposes, setting the host to localhost is better because it means only requests generated locally will be received: the app is not vulnerable to external threats. ===

More advanced configurations can also define whether the connections will be persistent and if not, the timeout they will have.

HTTP Listener

The HTTP listener is configured as an event source within flows and must reference the configuration to use and the path where it listens for requests.

<flow name="http-triggered-flow">
  <http:listener path="/" config-ref="HTTP_Listener_config"/>
  <set-payload value="Help! I've been triggered!"/>
</flow>

When an HTTP request matches a listener, its request data is used to trigger the flow. The body is set as the payload, while all other data from the request line to the headers are propagated as attributes. When the triggered flow is done, the HTTP listener uses its result to generate a proper HTTP response.

Paths

The path of an HTTP listener can be static, which requires exact matches, or feature placeholders. Placeholders can be wildcards (*), which match against anything they are compared to, or parameters ({param}), which not only match against anything but also capture those values on a URI parameters map.

Take the following example paths for three listeners using a configuration that establishes api/v1 as the base path:

  • account/mulesoft/main-contact: only match the exact path request http://awesome-company.com/api/v1/account/mulesoft/main-contact

  • account/{accountId}/main-contact: matches all path requests structured similarly, such as http://awesome-company.com/api/v1/account/salesforce/main-contact, and save salesforce as the value of accountId.

  • account/{accountId}/*: matches all path requests different from main-contact, such as http://awesome-company.com/api/v1/account/mulesoft/users, and save mulesoft as the value of accountId.

Wildcards at the end of a path can help capture requests to unhandled resources to provide better error messages.

When multiple listeners are defined, requests are always routed to the most specific one. Hence, in the previous examples, a request with accountId: mulesoft and suffix main-contact are handled by the first listener, and any different accountId value by the second listener instead of the third.

About Methods

Requests can also be routed based on the HTTP method received. By default, a listener handles all methods, but these can be restricted to the methods of your choosing, even custom ones. In the following example, GET, POST and PUT requests are routed to different flows. This is useful for restricting the visibility of data: you might allow certain users to see your data but only a few to modify it.

<flow name="main-contact-write">
  <http:listener path="account/{accountId}/main-contact" allowedMethods="POST, PUT" config-ref="HTTP_Listener_config"/>
  <!-- validate user permissions -->
  <!-- store or update main contact for accountId -->
</flow>

<flow name="main-contact-read">
  <http:listener path="account/{accountId}/main-contact" allowedMethods="GET" config-ref="HTTP_Listener_config"/>
  <!-- fetch main contact for accountId -->
</flow>

<flow name="main-contact-general">
  <http:listener path="account/{accountId}/main-contact" config-ref="HTTP_Listener_config"/>
  <set-payload value="#['The main contact resource does not support ' ++ attributes.method ++ ' requests.']"
</flow>

When multiple listeners are defined, requests are routed to the first listener matching the method, so default listeners should always be defined last.

From HTTP Request to Mule Message

The data from the HTTP request line, including the method, request path, query, and URI parameters, as well as the headers are all available as attributes within a triggered flow. On the other hand, the body and the Content-Type header are used to set up the payload and its mime type, which allows other components to inspect the payload mime type: DataWeave, for example, won’t need any input information to work with an HTTP payload. Additionally, if an X-Correlation-ID or MULE_CORRELATION_ID (for interoperability with Mule 3) header is present, it is used to set the message’s correlation ID for traceability.

Consider the following HTTP request:

POST api/v1/account/salesforce/main-contact?overwrite=true&notify=jane.doe&notify=admin HTTP/1.1
Host: localhost:8081
Content-Type: application/json
Content-Length: 166
X-Correlation-ID: 9cf32672-4f0b-4e8b-b988-40c13aae85b4

{
  "name": "John",
  "surname": "Doe",
  "role": "Senior Vice President",
  "organization": "Marketing",
  "phone": 701222369,
  "email": "john.doe@salesforce.com"
}

The message’s correlation ID is 9cf32672-4f0b-4e8b-b988-40c13aae85b4 and its payload is a JSON:

{
  "name": "John",
  "surname": "Doe",
  "role": "Senior Vice President",
  "organization": "Marketing",
  "phone": 701222369,
  "email": "john.doe@salesforce.com"
}

An expression like #[payload.name ' ' payload.surname] returns John Doe because DataWeave correctly interprets the JSON data.

The attributes of the message will include:

  • method: POST

  • listenerPath: api/v1/account/{accountId}/main-contact

  • requestPath: api/v1/account/salesforce/main-contact

  • relativePath: account/salesforce/main-contact

  • queryParams: a multi-map with entries overwrite ⇒ true, notify ⇒ jane.doe and notify ⇒ admin

  • uriParams: a map with entry accountId ⇒ salesforce

  • headers: a multi-map with entries host ⇒ localhost:8081, content-type ⇒ application/json, content-length ⇒ 166 and x-correlation-id ⇒ 9cf32672-4f0b-4e8b-b988-40c13aae85b4

Multi-maps are similar to maps except they allow several values for a given key. They will return the first value when using a single-value selector (.) but allow all values to be retrieved using the multiple-value selector (.*).

An expression like #['Received a ' attributes.method ' request for account ' attributes.uriParams.accountId '. The following users are notified: ' ++ (attributes.queryParams.*notify joinBy ', ')] returns Received a POST request for account salesforce. The following users are notified: admin, jane.doe.

See HTTP Documentation Reference for the complete and detailed listing of HTTP request attributes.

Multipart Form Example

The following example shows the manipulation of an HTTP listener event source message when an HTML form is received, such as:

<form action="http://server.com/cgi/handle"
        enctype="multipart/form-data"
        method="post">

    How would you like to identify the logo? <INPUT type="text" name="name"><BR>
    Which is the logo file? <INPUT type="file" name="logo"><BR>
    What is the main color in the logo? <INPUT type="text" name="color"><BR>
    <INPUT type="submit" value="Send"> <INPUT type="reset">

</form>

The resulting HTTP request when submitting the form is a multipart one:

POST /api/v1/account/mulesoft/logo HTTP/1.1
Content-Type: multipart/form-data; boundary=489691234097965980223899
Host: localhost:8081
content-length: 34332

--489691234097965980223899
Content-Disposition: form-data; name="name"

Corporate Logo
--489691234097965980223899
Content-Disposition: form-data; name="logo"; filename="MuleSoft_logo.png"
Content-Type: image/png

.PNG
.
...
IHDR.......L......~.....	pHYs...#...#.x.?v.. .IDATx....q.W.6.....~".N....t....t..#.....LD0T.CF0b..:.3......Q..@...q]U*y\c....
....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%
.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....V"H....`%.$....^6.......|..P.....IEND.B`.
--489691234097965980223899
Content-Disposition: form-data; name="color"

blue
--489691234097965980223899--

Each item can be accessed through the parts object, either by name or item number. For example, the second part can be accessed with payload.parts.logo or payload.parts[1]. The latter is useful when a name is not provided. Within each part, you can access its content and headers. For example, payload.parts.color.content returns blue, while payload.parts.logo.headers.'Content-Type' returns application/png. A very common scenario is obtaining the filename of a part. For this reason, the Content-Disposition header is parsed to allow an expression like payload.parts.logo.headers.'Content-Disposition'.filename, which in this case returns MuleSoft_logo.png.

See Formats Supported by DataWeave to learn more about reading and writing multipart content.

About HTTP Responses

After the triggered flow finishes its execution, the result is returned so that the listener can provide a response. If the flow executed successfully, a regular response is returned, but if the flow terminated with a failure, then an error response is returned. By default, a regular response returns a 200 status code and the message payload as the body, while an error response returns a 500 status code with the flow error’s description as the body. These responses can be customized by providing the:

  • Status code

  • Reason phrase

  • Headers multi-map

  • Body

DataWeave can be used to generate each parameter and variables can be used to propagate data from the flow.

In the following example, an endpoint is defined to store logos for an account:

  1. When the storage is successful, a 201 Created is returned with a body, such as Corporate Logo has been stored as a MuleSoft logo.

  2. When the storage fails, the status code is defined through the errorCode variable (if available) or 500 by default.

  3. A custom header is added (X-Time) as well.

  4. A body, such as Corporate Logo could not be stored, is returned.

  5. If there is a CONNECTIVITY error storing the logo, for example, the returned status code would be 504, while any other errors would result in a 500.

While a reason phrase is not defined for error responses, the connector attempts to define one based on the status code. Thus, a Gateway Timeout or Internal Server Error is returned in the scenarios explained before.
<flow name="store-logo">
  <http:listener config-ref="HTTP_Listener_config" path="/account/{accountId}/logo">
    <http:response statusCode="201" reasonPhrase="Created"> // 1
      <http:body ><![CDATA[#[output text/plain --- vars.logoName ++ ' has been stored as a ' ++ vars.accountId ++ ' logo.']]]></http:body>
    </http:response>
    <http:error-response statusCode="#[vars.errorCode default 500]"> // 2
      <http:body ><![CDATA[#[vars.logoName ++ ' could not be stored.']]]></http:body> // 3
      <http:headers ><![CDATA[#[
        output application/java
        ---
        {
          "X-Time" : "50s" // 4
        }
      ]]]></http:headers>
    </http:error-response>
  </http:listener>
  <set-variable variableName="logoName" value="#[payload.parts.name.content]"
  mimeType="text/plain"/>
  <set-variable variableName="accountId" value="#[attributes.uriParams.accountId]"
  mimeType="text/plain"/>
  <!-- store logo -->
  <error-handler >
    <on-error-propagate type="CONNECTIVITY"> // 5
      <set-variable variableName="errorCode" value="504"/>
    </on-error-propagate>
  </error-handler>
</flow>

HTTP Streaming Mode

When handling response bodies, the HTTP connector considers the kind of data to send and use chunked encoding when the size is not clear (think streams with no size information). You can change this behavior using the responseStreamingMode options:

  • AUTO (default): If a size is defined for the body, the listener uses Content-Length encoding. Otherwise it uses Transfer-Encoding: chunked.

  • ALWAYS: Uses Transfer-Encoding: chunked regardless of any size data present.

  • NEVER: Uses Content-Length encoding, consuming streams if necessary to determine the data size.

In the next example, the main contact data for an account always returns using Content-Length encoding.

<flow name="main-contact-read">
  <http:listener path="account/{accountId}/main-contact" allowedMethods="GET" responseStreamingMode="NEVER" config-ref="HTTP_Listener_config"/>
  <!-- fetch main contact for accountId -->
</flow>
View on GitHub