<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 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 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 requesthttp://awesome-company.com/api/v1/account/mulesoft/main-contact
-
account/{accountId}/main-contact
: matches all path requests structured similarly, such ashttp://awesome-company.com/api/v1/account/salesforce/main-contact
, and savesalesforce
as the value ofaccountId
. -
account/{accountId}/*
: matches all path requests different frommain-contact
, such ashttp://awesome-company.com/api/v1/account/mulesoft/users
, and savemulesoft
as the value ofaccountId
.
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¬ify=jane.doe¬ify=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
andnotify ⇒ admin
-
uriParams: a map with entry
accountId ⇒ salesforce
-
headers: a multi-map with entries
host ⇒ localhost:8081
,content-type ⇒ application/json
,content-length ⇒ 166
andx-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:
-
When the storage is successful, a
201 Created
is returned with a body, such asCorporate Logo has been stored as a MuleSoft logo
. -
When the storage fails, the status code is defined through the
errorCode
variable (if available) or500
by default. -
A custom header is added (
X-Time
) as well. -
A body, such as
Corporate Logo could not be stored
, is returned. -
If there is a
CONNECTIVITY
error storing the logo, for example, the returned status code would be504
, while any other errors would result in a500
.
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 usesTransfer-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>