The HTTP Server Transport Connectivity Plug-in

About the HTTP server transport

The HTTP server is a transport for use in connectivity plug-ins which external services can connect to over HTTP/REST. It can handle both an HTTP submission-only API which delivers events to the correlator and a REST request/response API where the responses are controlled from EPL. In addition to this, it can serve static files. It also allows support for TLS alongside HTTP basic authentication.

The HTTP server transport can decode HTTP requests and encode EPL responses or static files with gzip or deflate compression format. It also supports HTML form decoding and can decode multipart/form-data or application/x-www-form-urlencoded media types to a dictionary payload.

This transport provides a dynamic chain manager (the chain manager for each host/port is configured by an entry under dynamicChainManagers in the YAML configuration file) which creates chains automatically whenever an HTTP client connects to that host/port. There must be at least one chain definition provided in the dynamicChains section of the YAML configuration file. If providing more than one definition, the matchPathPrefix configuration option must be used to distinguish them. For more details, see Handling requests to different paths with different chains. The EPL channel that incoming requests are sent to is specified in the configuration of the dynamicChains, by rules in the mapperCodec section that set the metadata.sag.channel.

HTTP requests are received by the transport and sent to the chain where they are mapped to EPL events as described in Mapping events between EPL and HTTP server requests. Whether the response to the HTTP request is generated automatically or by the EPL application is controlled as described in Handling responses in EPL.

For more information on YAML configuration files, see Using connectivity plug-ins and especially Configuration file for connectivity plug-ins.

Persistent connections to the server are supported for multiple requests. Details of the individual requests are configured through the events sent to the chain. The HTTP server supports HTTP version 1.1 and TLS version 1.2 and above.

The HTTP server is designed to listen for REST services and supports all GET, POST, PUT and DELETE operations which have been specified in the configuration file. Other than GET requests served by static files, all requests are treated identically.

The samples/connectivity_plugin/application/httpserver directory of your Apama installation includes a sample which demonstrate how to use the HTTP server connectivity plug-in to send and receive HTTP requests containing events into the correlator through various configurations. See the README.txt file included with the sample for complete instructions on how to run the sample application.

Info
The HTTP server connectivity plug-in does not support reliable messaging.
OpenAPI definitions

OpenAPI is an open description format for REST APIs. The OpenAPI Specification (OAS), and the related tools available from Swagger (https://swagger.io), can be used to design, document, deploy and test the REST API for an application. The specification allows for API definitions to be written in either YAML or JSON.

Apama API definitions are supplied in JSON format to the OpenAPI/Swagger 3.0 specification.

HTTP response codes

The transport returns a response to the client. If responses are automatically generated, we return a “202 Accepted” response after HTTP parsing, but before processing by the correlator, to indicate that a failure may still occur later in processing the event. If the response is handled by EPL, the response code is defined by the EPL application and configuration. If there is a failure in parsing the HTTP part of the request, an error code is returned instead.

The various response codes that we currently support are described below.

Code Reason
202 Accepted Success response code for automatic responses. On a successful submission, this indicates that while we have accepted it, processing will occur later and we cannot guarantee completion.
400 Bad Request Any other error we can conclusively say is due to a malformed request.
401 Unauthorized We have enabled HTTP basic authentication and the user either does not supply an Authorization header or it is incorrect.
405 Method Not Allowed The request has a method we do not support (depending on what is configured in the configuration file).
413 Request Entity Too Large The uncompressed payload is larger than defined with the maxRequestBytes configuration option. See Configuring the HTTP server transport for more information on this configuration option.
415 Unsupported Media Type The client has sent an unsupported Content-Encoding header.
429 Too Many Requests If too many authentication failures occur (maxAttempts), then requests are throttled for the defined cool-down period (coolDownSecs) to protect the running correlator. See Configuring the HTTP server transport for more information on the configuration options maxAttempts and coolDownSecs.
500 Internal Server Error Any other error which occurs before we send the event into the correlator.
503 Host Not Ready The HTTP server received a request before the application called onApplicationInitialized() in the correlator. See Sending and receiving events with connectivity plug-ins for more information on this method.
504 Gateway Timeout The EPL application did not respond within the configured timeout.
Other HTTP response codes As defined by the EPL application and configuration.

Loading the HTTP server transport

Loading the HTTP server transport

You can load the HTTP server transport by adding the HTTP Server connectivity bundle to your project in Apama Plugin for Eclipse (see Adding the HTTP server connectivity plug-in to a project) or using the apama_project tool (see Creating and managing an Apama project from the command line). Alternatively, you can load the transport with the following connectivityPlugins stanza in your YAML configuration file:

connectivityPlugins:
    HTTPServerTransport:
        libraryName: connectivity-http-server
        class: HTTPServer

Configuring the HTTP server transport

The HTTP server has a manager that deals with connections and one or more chains that deal with mapping events into the correlator. All chains defined are used by all managers. If you require multiple ports (that is, with different options), then you need multiple managers. The HTTP server should be added to a manager and chain containing the appropriate mapping rules (see Mapping events between EPL and HTTP server requests for detailed information).

Manager

Example:

dynamicChainManagers:
    HTTPServerManager:
        transport: HTTPServerTransport
        managerConfig:
            port: 15910
            bindAddress: 10.13.23.125
            tls: false
            tlsKeyFile: ${PARENT_DIR}/servername.key.pem
            tlsCertificateFile: ${PARENT_DIR}/servername.cert.pem
            connectionTimeoutSecs: 60
            maxConnections: 16
            keepAliveTimeSecs: 120
            concurrentChains: true
            staticFiles:
                /swagger.json:
                    file: ${PARENT_DIR}/swaggerDefault.json
                    contentType: application/json
                    charset: utf-8

The following configuration options are available for the manager on the HTTP server:

Configuration option

Description

port

Required. The user-defined port on which the server is accessible. Type: integer.

bindAddress

Optional. Binds to specific interfaces, potentially on multiple ports. Each entry is either a host, or a host:port combination. If a port is provided, it is used. Otherwise, the port option applies. The default is to bind to all interfaces on the configured port. Type: string or list<string>.

Default: blank.

tls

Optional. Set this to true to enable TLS (https). Type: bool.

Default: false.

tlsKeyFile

The private key for the certificate in PEM format. Required if TLS is enabled. Type: path.

tlsCertificateFile

The server certificate file in PEM format. Required if TLS is enabled. Type: path.

connectionTimeoutSecs

Maximum time to handle a single request before returning a timeout (in seconds). Type: integer.

Default: 60.

maxConnections

Maximum number of simultaneous connections which can be handled. Type: integer.

Default: 16.

keepAliveTimeSecs

Optional. Set this to the maximum idle time in seconds between requests on a persistent connection before it is closed. If not set, the default value is used. Type: integer.

Default: 15.

concurrentChains

Optional. Set this to true to enable concurrent chains where each connection uses a different chain into the HTTP server to process requests and responses, up to a maximum of maxConnections. Requests on the same connection are processed in order. If set to false (default), concurrent chains are disabled. A single chain is used for all connections, and it only processes a single request at a time.

Type: bool.

Default: false.

staticFiles

Optional. Map of static files. Elements are of the form: ``` /url: file: ${PARENT_DIR}/source_file.txt contentType: text/plain charset: utf-8

file and contentType are required, charset is optional.

Type: Map.

Default: undefined.

Chain

Example:

dynamicChains:
    HTTPServerChain:
        - apama.eventMap
        *mapping rules...*
        - HTTPServerTransport:
            authentication:
              authenticationType: none
              allowedUsersFile: ${PARENT_DIR}/userfile.txt
              maxAttempts: 5
              coolDownSecs: 30
            automaticResponses: false
            responseCompression: "ifRequested"
            responseTimeoutMs: 5000
            matchPathPrefixes: [""]
            allowedMethods: [PUT]

The following configuration options are available for the chain on the HTTP server:

Configuration option

Description

authentication/authenticationType

Set this to HTTP_BASIC if you require HTTP basic authentication. Type: HTTP_BASIC or none.

Default: none.

authentication/allowedUsersFile

Path to the password file (see Authentication). Required if the authentication type is HTTP_BASIC. Type: path.

authentication/maxAttempts

Maximum number of failed login attempts before throttling the requests for that user. See Authentication for more information. Type: integer.

Default: 3.

authentication/coolDownSecs

The number of seconds after the maximum number of failed login attempts before the HTTP server attempts authentication of the user again. See Authentication for more information. Type: integer.

Default: 20.

automaticResponses

Set this to true if you want a submission-only API where the responses are generated automatically by the transport. If set to false, the transport will wait for a response from the EPL application, subject to a timeout. Type: bool.

responseCompression

The Accept-Encoding header is used for negotiating content encoding. Set this to ifRequested if you want to encode an EPL response or a static file. If set to never, no encoding is applied to the entity-body. Type: string.

Default: never.

responseTimeoutMs

The number of milliseconds we wait for a response from the EPL application before returning to the client. Type: integer.

Default: 5000 (5s).

matchPathPrefixes

If providing multiple chains in the dynamicChains section, you must provide a matchPathPrefixes option on all of them to specify which chain should handle each request. This specifies a list of prefixes to the HTTP request path. The longest matching prefix is used to handle a given request. Required if using multiple chains. See Handling requests to different paths with different chains for more information.Type: string or list<string>.

allowedMethods

Required. List of permitted HTTP verbs (for example, PUT or GET). Type: string or list<string>.

maxRequestBytes

Maximum permitted HTTP payload size in bytes. Type: integer.

Default: 1048576 (1MB).

Handling requests to different paths with different chains

You can provide the HTTP server with multiple chain configurations specifying different mapping rules for different requests. These rules are differentiated based on the path of the HTTP request. To do this, provide more than one entry in the dynamicChains section of your YAML configuration file, each finishing with an instance of the HTTP server transport. To use this feature, you must provide a distinct matchPathPrefixes configuration option on each of the transports. This is a list of string prefixes which are handled by the given chain. Typically, one of the chains has a default entry of "" (the empty string) which handles all requests not handled by any other chain.

For example:

dynamicChains:
   DefaultHTTPServerChain:
      - apama.eventMap
      mapping rules...
      - HTTPServerTransport:
           allowedMethods: [GET]
           automaticResponses: false
           matchPathPrefixes: ""
   RESTHTTPServerChain:
      - apama.eventMap
      mapping rules...
      - HTTPServerTransport:
           allowedMethods: [GET]
           automaticResponses: false
           matchPathPrefixes: ["/rest", "/api"]
   CRUDHTTPServerChain:
      - apama.eventMap
      mapping rules...
      - HTTPServerTransport:
           allowedMethods: [GET,PUT,DELETE,POST]
           automaticResponses: false
           matchPathPrefixes: ["/rest/objects"]

In the above example, requests to /rest/objects are handled by the CRUD chain. Requests to other places under /rest or /api are handled by the REST chain. Anything else is handled by the default chain. Each chain can provide different mapping rules for different formats of request, mapping to different EPL events and delivering to different channels.

The HTTP server lazily instantiates chains of each type for each incoming thread (if using the concurrentChains option), so they are not created until a matching request comes in on a given thread.

If there are two chains with overlapping prefixes, the prefix with the longest match is chosen. For example, if the first chain specifies /data and the second chain specifies /data/instances, then a request to /data/global uses the first chain, but a request to /data/instances/12345 uses the second chain.

Queueing behavior

You can configure each chain to deliver to separate channels. This potentially allows that if requests to one of the channels are backed up or blocked, requests to independent paths can still be processed. It also means that ordering is not guaranteed across different chains. Each chain has its own response channel, which is mapped into the chain using the @{httpServer.responseChannel} variable when it is created.

If you want to ensure that requests using one chain do not block requests using another chain, then you must do the following:

  1. Ensure requests on each chain are delivered to a different channel, possibly by setting a different defaultChannel configuration property on each host plug-in.
  2. Subscribe different contexts to each channel.
Logging and metrics

In order to distinguish the different chains in log message and Prometheus metrics, a label of {template=nameOfDynamicChain} is added to the name of the chain and any metrics in that chain.

Handling responses in EPL

In order to have the response to an HTTP request handled by your EPL application, you need to configure the HTTP server chain correctly and then respond to the event delivered to your application. The transport must have automaticResponses set to false in the configuration (see also Configuring the HTTP server transport), and it must map the following variables into the message to be able to send responses.

  • metadata.requestId

    This variable is set by the transport for every message. Responses must also have the same metadata.requestId set. This is normally done by mapping it to a payload field in your request for the message sent to the host and then back into the metadata for the response.

  • @{httpServer.responseChannel}

    This variable is set when creating the chain. This should be set in your request messages. It tells the EPL application to which channel responses should be sent back. Responding messages should also set metadata.http.statusCode correctly.

    Info
    You must send the response to the channel specified in the corresponding request event. The channel name is not guaranteed to be constant even within a single manager.

For example:

dynamicChains:
  HTTPServerChain:
    - apama.eventMap:
        defaultEventType: RequestEvent
        defaultChannel: requests
    - mapperCodec:
        "*":
           towardsHost:
              mapFrom:
                 - payload.requestId: metadata.requestId
              defaultValue:
                 - payload.responseChannel: "@{httpServer.responseChannel}"
           towardsTransport:
              mapFrom:
                 - metadata.requestId: payload.requestId
              defaultValue:
                 - metadata.http.statusCode: 200
     - jsonCodec
     - stringCodec
     - HTTPServerTransport:
         automaticResponses: false
         allowedMethods: [ PUT ]

Your EPL application must then respond to messages, preserving the requestId and responding on the correct channel. For example:

on all RequestEvent() as re {
   any data := // do something to get the response data
   send ResponseEvent(re.requestId, data) to re.responseChannel;
}
Info
The request and response events given here are examples. You must define your own events appropriate to your application. For more examples, see Examples.

If a response is not received by the transport within the configured timeout, then the transport returns a “504 Gateway Timeout” response. This timeout can be configured with the responseTimeoutMs configuration option (see also Configuring the HTTP server transport).

The response messages must be converted and mapped using the chain configuration to meet the following requirements:

  • The response payload is a binary message. This will probably be created using the String codec from the event.
  • The metadata.http.statusCode variable is set. This will usually be set to 200 by the Mapper codec.
  • The metadata.contentType and metadata.charset variables are set. These will usually be set by the JSON codec and String codecs when in use, but can also be set by the Mapper codec.

In addition, you can set other HTTP headers. For more details, see Mapping events between EPL and HTTP server requests.

Serving static files

The HTTP server allows you to serve static files from disk. You can list the static file URI which will be available using a GET request, and it will be served by that file. GET requests that match a static file do not get passed into the correlator.

Static file requests do not go through the checks that all other requests go through, which are:

  • Transport status (host ready)
  • HTTP basic authentication
  • Allowed methods
  • Maximum request size

You must list static files individually in the configuration file, and you must provide the MIME type of the file being served. Optionally, you can also provide the charset type.

staticFiles:
    /swagger.json:
        file: ${PARENT_DIR}/swagger.json
        contentType: application/json
        charset: utf-8

Mapping events between EPL and HTTP server requests

The HTTP server can either be used as a general event submission API or as a general request/response API. A request to the HTTP server contains either a binary payload or a dictionary payload if the request had either an application/x-www-form-urlencoded or multipart/form-data content type. In the latter case, there will also be additional metadata fields. For the requests to be useful to EPL, they must be converted into the format expected by Apama. This is done using the Classifier codec, Mapper codec and other codecs (see Codec connectivity plug-ins). For request/response APIs, the same process is used in reverse to turn EPL events into the responses.

The event types used in EPL should be specific to your application and then mapped in the chain from the fields produced by the HTTP server. The following fields are created in each event by the HTTP server. Field names containing periods (.) indicate nested map structures within the metadata. This nesting is automatically handled by the Mapper codec, and fields can be referred to there just using these names (see also The Mapper codec connectivity plug-in).

The fields for requests from the transport to EPL are:

Field Description
payload The binary payload of the request.
metadata.requestId A unique integer identifier which must be preserved in the response when using EPL-supplied responses.
metadata.contentType The MIME type of the payload (string), taken from the first parameter of the HTTP Content-Type header, converted to lower case and with spaces trimmed off. See also Handling HTTP headers.
metadata.charset The charset parameter of the Content-Type header (string), converted to lower case, with spaces trimmed off. See also Handling HTTP headers.
metadata.http.path The path component (string) of the URI.
metadata.http.method The HTTP method of the request: PUT, POST, GET, or DELETE.
metadata.http.user When HTTP basic authentication is enabled, the authenticated user name (string).
metadata.http.cookies A key-value map of cookies from the request (map). See also Dealing with cookies.
metadata.http.queryString A key-value map of the options in the query-string component of the request URI (map). This field is only set in the request when the query string is not empty. See also Providing HTTP query parameters.
metadata.http.headers A key-value map of the HTTP headers sent by the request (map). Key names are converted to lower case regardless of original capitalization. See also Handling HTTP headers.
metadata.http.source The address and port of the client connection which generated this request.

The fields for EPL-supplied responses are:

Field Description
payload The binary or dictionary payload of the response.
metadata.requestId The requestId of the corresponding request. Must be correctly set in responses.
metadata.contentType The MIME type of the payload (string). This is used to construct the HTTP Content-Type header. See also Handling HTTP headers.
metadata.charset The charset of the payload, for text-format payloads. This is used to construct the HTTP Content-Type header. See also Handling HTTP headers.
metadata.http.statusCode The HTTP status code (integer). Must be set in responses.
metadata.http.cookies A key-value map of cookies to set on the client. See also Dealing with cookies.
metadata.http.headers A key-value map of additional HTTP headers to send in the response. See also Handling HTTP headers.
metadata.http.form.name.contentType The media type of the form data. See also Handling HTTP form decoding.
metadata.http.form.name.charset The encoding of the form data. See also Handling HTTP form decoding.
metadata.http.form.name.filename The filename of the form data. See also Handling HTTP form decoding.

Distinguishing request types

A single chain will often deal with multiple event types received within requests. For messages towards the host, the event type will not yet have been set. The Mapper and Classifier codecs can use fields in the message (payload or metadata) to set the event type.

You can write the configuration to behave in whatever way you like. There are several ways of determining to which event type the request corresponds. In the default configuration that we supply, the event type is provided as part of the request, but it is also possible to infer the event type from the content of the request.

Below are some examples of what is possible.

You can use the Mapper codec to set the type and channel from the payload as shown below. The type is part of the request. The Mapper code assigns it to metadata.sag.type.

- mapperCodec:
    "*":
        towardsHost:
            mapFrom:
                - metadata.sag.type: payload.type
                - metadata.sag.channel: payload.channel
                - payload: payload.data

You can use the Classifier codec to determine the event type based on incidental fields in the event, such as the method and path:

- classifierCodec:
    rules:
        - KickEvent:
            - metadata.http.method: GET
            - metadata.http.path: /kick
        - DocumentSubmissionEvent:
            - metadata.http.method: PUT
            - metadata.http.path: /submit
        - DocumentUpdateEvent:
            - metadata.http.method: PUT
            - metadata.http.path: /update

The default event type is generally used if all events received in requests are the same:

- apama.eventMap:
    defaultEventType: TestEvent

You can use regular expressions with the Classifier codec to match more than one REST URL to a single event type. The following example shows a rule that matches two different REST URLs such as /database/emptable/78451339 and /database/managertable/50044897:

- classifierCodec:
    rules:
        - com.apama.swagger.ISSPositionResponse:
            - regex:metadata.http.path: /database/[a-zA-Z0-9]*/[0-9]*

For detailed information on these codecs, see The Mapper codec connectivity plug-in and The Classifier codec connectivity plug-in.

Handling HTTP headers

The HTTP server reads any number of headers from the received request and puts them into metadata.http.headers. Similarly, when using EPL-supplied responses, headers are read from metadata.http.headers and written into the response as individual HTTP header lines. Some special handling is applied as described below.

All HTTP headers are converted from ISO-8859-1 (the character set for HTTP headers as defined in the RFC publications) to UTF-8 in the metadata and vice-versa.

All HTTP header keys are converted to lowercase (since HTTP header keys are defined to be case-insensitive). You should use lowercase in all of your mapping and classification rules.

Any HTTP headers for which multiple values have been provided for a single key (after normalization of case) are dropped.

The content type and charset in requests, which are parsed from the Content-Type header, are provided in metadata.contentType and metadata.charset respectively. For responses, the two metadata fields are combined into the Content-Type header.

If HTTP basic authentication is enabled, then the authorization header is removed from metadata.http.headers, but in this case the user name is still available in metadata.http.user. If authorization is none, then the authorization type is passed through verbatim.

All cookies in requests are put into the metadata.http.cookies field and that field is used to generate Set-Cookie headers in responses. See also Dealing with cookies.

To protect the security of personal data, see Protecting personal data in Apama applications.

Handling HTTP form decoding

The HTTP server transport decodes multipart/form-data or application/x-www-form-urlencoded media types to a dictionary payload.

If the Content-Type header field contains the application/x-www-form-urlencoded media type, the request payload is decoded to a dictionary payload with string keys and string values.

If the Content-type header field contains the multipart/form-data media type, the request payload is decoded to a dictionary payload with string keys and either string or binary values.

For the parts that have binary data, additional metadata is created. This metadata contains the contentType, charset and filename information for each binary part.

You can get the metadata as follows:

metadata.http.form.name.contentType
metadata.http.form.name.charset
metadata.http.form.name.filename

where name corresponds to the data in payload.name.

Simple example

In this example, a client sends HTTP POST requests to the HTTP server transport and the Content-Type header is set to multipart/form-data. The request payload contains two form fields, one field has both a string key and string value, and the other field has a string key and binary value.

Simple raw HTTP POST request:

POST http://localhost:80/
Content-Length: 737
Content-Type: multipart/form-data; boundary=--123456789
--123456789
Content-Disposition: form-data; name="foo"
bar
--123456789
Content-Disposition: form-data; name="file"; filename="file.txt"
Content-Type: text/plain; charset=utf-8
File data
--123456789--

For the above request, the HTTP server transport sends a dictionary payload({"foo":"bar", "file":File data}) to EPL.

Metadata created for the file parts have text/plain as the content type, utf-8 as the character set, and file.txt as the filename. You can map the metadata using the Mapper codec:

- mapperCodec:
    "*":
      towardsHost:
        mapFrom:
          - payload.contentType: metadata.http.form.file.contentType
          - payload.charset: metadata.http.form.file.charset
          - payload.filename: metadata.http.form.file.filename

Parts metadata is only created for binary or file-upload form-data.

Mapping the body

The HTTP server accepts the payload as a binary object. What the payload consists of depends on the service you wish to provide. Many services use string-based protocols (such as JSON). For these types of payload, you can use the String codec (see The String codec connectivity plug-in). For messages towards the host, the String codec takes a byte array and decodes it to a string using the UTF-8 encoding. If you are using the String codec, you should put it as the last codec before the HTTP server.

The resulting string can then be mapped directly into a field in an EPL event, or it can be further processed by other codecs (such as the JSON codec, as used in our default configuration) before the resulting fields are mapped into the Apama event.

If you need to vary your processing depending on the type of the data received, you may need to write a custom codec in order to handle this. To help with distinguishing different payload types, the HTTP server sets top-level fields to indicate the type of the payload. The HTTP header indicates the MIME type populated into metadata.http.contentType. If present, then the character set from the same HTTP header is copied into metadata.http.charset.

When using EPL-supplied responses, the mapping rules must be bidirectional to map both the request and the response.

Dealing with cookies

The HTTP server stores cookies in metadata.http.cookies.keyname entries.

In requests, the HTTP server takes any number of HTTP Cookie headers and turns them into corresponding metadata.http.cookies entries. You can either map the entire set of cookies to a dictionary field in an event, or you can map a specific cookie key to a field in an event.

In responses, the HTTP server adds Set-Cookie headers for each entry in metadata.http.cookies. You must use the Mapper codec to map things from your response events into the metadata entries.

Providing HTTP query parameters

HTTP requests can be set to contain request parameters, which are encoded at the end of the URL in the following form:

/path?key=value&key=value

The request parameters are decoded and added to the metadata.http.queryString map as key-value pairs. The parameters can either be mapped to a dictionary field in an event, or a specific named parameter can be mapped to a single field. For example:

- mapperCodec:
    Request:
      towardsHost:
        mapFrom:
          # set one query parameter individually
          - payload.paramValue: metadata.http.queryString.param
          # alternatively set all query parameters in an EPL dictionary
          - payload.parameters: metadata.http.queryString

The metadata.http.queryString field is only set in the request when the query string is not empty. If you wish to map the query string to an event field and there is a chance it could be empty, add a defaultValue for it in your mapperCodec rules. See also The Mapper codec connectivity plug-in.

Examples

Generic engine_send HTTP service

This example is the same as the default configuration supplied with Apama.

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
    - apama.eventMap
    - mapperCodec:
        "*":
          towardsHost:
             mapFrom:
               - metadata.sag.type: payload.type
               - metadata.sag.channel: payload.channel
               - payload: payload.data
    - jsonCodec
    - stringCodec
    - HTTPServerTransport:
        authentication:
          authenticationType: none
          allowedUsersFile: ${PARENT_DIR}/userfile.txt
        automaticResponses: true
        allowedMethods: [PUT]

EPL:

event Temperature
{
   integer sensorId;
   string sensorName;
   float temperature;
   dictionary<string,any> extra;
}

monitor.subscribe("myChannel");
on all Temperature() as e {
  //...
}

Curl example:

curl -X PUT http://localhost:8080/ -d '{"type":"Temperature",
 "channel":"myChannel", data:{"sensorId":666, "sensorName":"FooBar",
 "temperature":3.14",{"A":"alpha"}} }' -H "Content-Type:application/json"
Event type and channel information is specified in headers

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
    - apama.eventMap
    - mapperCodec:
        "*":
          towardsHost:
             mapFrom:
               - metadata.sag.type : metadata.http.headers.x-apamaeventtype
               - metadata.sag.channel : metadata.http.headers.x-apamachannel
    - jsonCodec
    - stringCodec
    - HTTPServerTransport:
        authenticationType: none
        allowedUsersFile: ${PARENT_DIR}/userfile.txt
        automaticResponses: true
        allowedMethods: [PUT]

EPL:

event Temperature
{
   integer sensorId;
   string sensorName;
   float temperature;
   dictionary<string,any> extra;
}

monitor.subscribe("myChannel");
on all Temperature() as e {
  //...
}

Curl example:

curl -X PUT -H "X-ApamaEventType:Temperature"  -H "X-ApamaChannel:myChannel"
 http://localhost:8080/ -d '{"sensorId":666, "sensorName":"FooBar",
 "temperature":3.14",{"A":"alpha"} }'  -H "Content-Type:application/json"
Event type and channel information is specified in the query string

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
    - apama.eventMap
    - mapperCodec:
        "*":
          towardsHost:
            mapFrom:
              - metadata.sag.type : metadata.http.queryString.eventType
              - metadata.sag.channel : metadata.http.queryString.channel
    - jsonCodec
    - stringCodec
    - HTTPServerTransport:
        authenticationType: none
        allowedUsersFile: ${PARENT_DIR}/userfile.txt
        automaticResponses: true
        allowedMethods: [PUT]

EPL events:

event Temperature
{
   integer sensorId;
   string sensorName;
   float temperature;
   dictionary<string,any> extra;
}

monitor.subscribe("myChannel");
on all Temperature() as e {
  //...
}

Curl example:

curl -X PUT 'http://host:port/submit?eventType=Temperature&channel=myChannel'
 -d '{"sensorId":666, "sensorName":"FooBar", "temperature":3.14",{"A":"alpha"} }'
Event types are tied to the method and path and the channel is defaulted

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
    - apama.eventMap
    - mapperCodec:
         KickEvent:
           towardsHost:
              - metadata.sag.channel: kickEvents
         DocumentSubmissionEvent:
           towardsHost:
              mapFrom:
                 - payload.data: payload
              defaultValue:
                 - metadata.sag.channel: submissionEvents
         DocumentUpdateEvent:
           towardsHost:
              mapFrom:
                 - payload.data: payload
              defaultValue:
                 - metadata.sag.channel: updateEvents
    - classifierCodec:
         rules:
            - KickEvent:
                 - metadata.http.method: GET
                 - metadata.http.path: /kick
            - DocumentSubmissionEvent:
                 - metadata.http.method: PUT
                 - metadata.http.path: /submit
            - DocumentUpdateEvent:
                 - metadata.http.method: PUT
                 - metadata.http.path: /update
    - stringCodec
    - HTTPServerTransport:
        authenticationType: none
        allowedUsersFile: ${PARENT_DIR}/userfile.txt
        automaticResponses: true
        allowedMethods: [PUT, GET]

EPL events:

event KickEvent { }
event DocumentSubmissionEvent { string data; }
event DocumentUpdateEvent { string data; }
Delivering Apama event strings

This example is using the string form of the event native to Apama. You should only use this example if you have a system that encodes events in that format.

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
    - apama.eventString
    - mapperCodec:
       "*":
         towardsHost:
           mapFrom:
             - metadata.sag.channel: metadata.http.path
    - stringCodec
    - HTTPServerTransport:
         authenticationType: none
         allowedUsersFile: ${PARENT_DIR}/userfile.txt
         automaticResponses: true
         allowedMethods: [PUT]

EPL:

monitor.subscribe("/channel/ChannelName");
on all Temperature() as e {... }

Curl example:

curl -X PUT http://host:port/channel/ChannelName  -d 'Temperature(10, "Baz",
 6.022e23)'
EPL-controlled responses

This example generates responses to the HTTP requests in EPL. Requests should be JSON objects containing objectId and requestType. Responses are arbitrary JSON objects. See also Handling responses in EPL.

YAML dynamic chain:

dynamicChains:
  HTTPServerChain:
     - apama.eventMap:
          defaultChannel: requests
          defaultEventType: HTTPRequest
     - mapperCodec:
          "*":
             towardsHost:
                mapFrom:
                   - payload.requestId: metadata.requestId
                defaultValue:
                   - payload.channel: "@{httpServer.responseChannel}"
             towardsTransport:
                mapFrom:
                   - metadata.requestId: payload.requestId
                   - payload: payload.responseData
                defaultValue:
                   - metadata.http.statusCode: 200
     - jsonCodec
     - stringCodec
     - HTTPServerTransport:
          automaticResponses: false
          allowedMethods: [ PUT ]

EPL:

monitor.subscribe("requests");
on all HTTPRequest() as r {
    send HTTPResponse(r.requestId, getResponseData(r.requestType, r.objectId))
         to r.channel;
}

EPL events:

event HTTPRequest {
    integer requestId;
    integer objectId;
    string requestType;
    string channel;
}
event HTTPResponse {
    integer requestId;
    any responseData;
}

HTTP server security

TLS

We provide TLS-based security with the HTTP server and we recommend that you use this in production. In order to be compatible with our system, you must use TLS version 1.2 or above.

We also recommend that your internet deployment is behind a reverse proxy for optimum security.

In order to use this, you must enable TLS in the YAML configuration file and supply a TLS server certificate file and corresponding key file, as shown in the following example:

dynamicChainManagers:
    HTTPServerManager:
        transport: HTTPServerTransport
        managerConfig:
            port: 443
            tls: true
            tlsKeyFile: ${PARENT_DIR}/servername.key.pem
            tlsCertificateFile: ${PARENT_DIR}/servername.cert.pem

Authentication

Info
HTTP basic authentication is not applied to static file requests. See Serving static files.

HTTP basic authentication support is provided by comparing the request authentication contents against an authentication password file supplied during configuration. We recommend that you only use this if you also have TLS enabled. For more complex use cases, webMethods Integration Server should be used.

If you are using HTTP basic authentication, you must provide a valid authentication password file using the allowedUsersFile configuration option.

This password file expected by the HTTP server for HTTP basic authentication is compatible with the output of Apache’s htpasswd -B. There is also a bundled application called httpserver_passman which can create and update password files. You can find the executable for this tool in the bin folder of your Apama installation. The syntax for this is:

httpserver_passman password_file [options] username [password]

If you only provide a username and no password, then the password is prompted for interactively. This adds the specified user with the given password, or replaces the password if the user already exists in the password file.

The options are:

Option

Description

-h | --help

Displays usage information.

-c | --createNew

Creates a new file and overwrites anything currently there.

-D | --delete

Deletes the given user, rather than updating or adding the user.

--

Does not treat subsequent arguments as options. Thus, it is possible to enter a username that starts with one or two minus signs.

If HTTP basic authentication is enabled, then the authorization header is removed from metadata.http.headers, but in this case the user name is still available in metadata.http.user. If authorization is none, then the authorization type is passed through verbatim.

Info
Enabling authentication significantly reduces the maximum achievable throughput on a single connection since HTTP_BASIC requires verifying credentials on every request. It is not suitable for high-throughput applications.

Using the configuration options maxAttempts and coolDownSecs, you can protect against brute force attacks on users and passwords (see also Configuring the HTTP server transport). The initial response to a failed authentication attempt is a “401 Unauthorized” response. This response occurs until the defined number of failed login attempts (maxAttempts) has been reached. After this, the HTTP server ignores authentication attempts for the defined cool-down period (coolDownSecs). During that period, the HTTP server returns “429 Too Many Requests” with a reason of “Too many failed authentication requests, please try again later.”. When the cool-down period has expired, the HTTP server attempts to authenticate any further request. If it fails that attempt, the user is immediately placed back into a cool-down period without retries.

Info
Requests from unknown users are treated in the same way as requests from allowed users to avoid user information leakage.

To protect the security of personal data, see Protecting personal data in Apama applications.

Monitoring status for the HTTP server

The HTTP server component provides status values via the user status mechanism. It provides the following metrics (where prefix is the name of the dynamic chain manager, typically HTTPServerManager):

Key Description
prefix.status Moves from STARTING to ONLINE when hostReady is called.
prefix.eventsTowardsHost Number of requests resulting in events being sent to the correlator. This is the primary KPI for this component.
prefix.failedRequests Number of non-2xx responses sent to clients, including errors generated from EPL. This is expected to be 0 and is a KPI with a warning threshold at 1.
prefix.staticFileRequests Number of static files served to clients. This is a KPI.
prefix.authenticationFailures Number of requests with invalid credentials.
prefix.numChains Number of active chains for connections into this HTTP server instance. The chains can be reused between connections, but a single connection only uses one chain. This is expected to be between 0 and the maximum number of simultaneous connections which can be handled (see also maxConnections in Configuring the HTTP server transport).
prefix.requestSizeEWMAShortBytes A quickly-evolving exponentially-weighted moving average of request sizes, in bytes.
prefix.requestSizeEWMALongBytes A longer-term exponentially-weighted moving average of request sizes, in bytes.
prefix.requestSizeMaxInLastHourBytes The maximum request size in bytes since the start of the last 1 hour measurement period.
prefix.responseSizeEWMAShortBytes A quickly-evolving exponentially-weighted moving average of response sizes, in bytes.
prefix.responseSizeEWMALongBytes A longer-term exponentially-weighted moving average of response sizes, in bytes.
prefix.responseSizeMaxInLastHourBytes The maximum response size in bytes since the start of the last 1 hour measurement period.

For each request/response that is processed, the above MaxInLastHour values are updated if either of the following conditions is true:

  • The size of the current message is greater than the existing maximum.
  • The existing maximum value was set more than 1 hour ago.

Automatic responses are not included in the response size metrics.

Error responses are not included in the response size metrics. The request size metrics are calculated before compression and the response size metrics are calculated after decompression.

For more information about monitor status information published by the correlator, see Managing and monitoring over REST and Watching correlator runtime status.