HttpCaller

The HttpCaller is a Linux application for making outgoing HTTP calls. It reads instructions from a RabbitMQ message queue, and turns them into HTTP POST calls. We wrote this application for MailerQ users who asked for a solution to send the results of the deliveries to a HTTP endpoint. It is however a generic application: it can be used for all sort of calls to HTTP endpoints.

Installation

The application is stored in our APT repository. If you have already enabled our APT repository, you can install it with this command:

sudo apt install mailerq-httpcaller

Integration with MailerQ

A typical use case is to route the MailerQ delivery results to this application. However, MailerQ output is not compatible with HttpCaller input. The output of MailerQ must thus first be converted. We have a special application for this: mailerq-amqphttp. There is also a simple installation script that runs the entire stack of the httpcaller and the converter: mailerq-webhook.

Running

You typically run this program from the command line. It reads all its input from a config file stored in /etc/copernica/httpcaller.txt. However, you can also pass command line arguments, for example:

httpcaller --rabbitmq-address amqp://user:password/hostname/vhost

Configuration

All options can be supplied in the system wide config file, via environment variables, and as command line options. The config file is stored in /etc/copernica/httpcaller.txt. The following options are all equivalent:

All other configuration settings can be converted in a similar manner.

Supported options for RabbitMQ

HttpCaller reads all instructions from a RabbitMQ message queue, and writes the results back to RabbitMQ. The following options can be used to configure this:

HttpCaller needs access to a RabbitMQ server via the AMQP address to consume and publish messages. But because the AMQP lacks the ability to find out which queues are active you must also specify the REST endpoint of your RabbitMQ server.

All messages are read from the configured queue, and the results (whether the HTTP POST call was a success or not) are published back to an exchange using routing keys "delayed", "failure" or "success". If you do not like the default routing keys, you can override them with other values.

If there is a big queue with outgoing POST calls for one specific domain, HttpCaller temporarily creates an overflow queue and parks all those calls in that queue. By doing so, it can still process other messages from the same queue for other domains. This prevents that lag for one domain limits the rate for other domains. To disable this feature, you can set the "overflow-queues" option to false.

Capacity settings

HttpCaller can send out many HTTP POST calls at the same time. The limits are configurable:

The host connections limit can be overridden on a per-call bases.

Signing outgoing connections

Outgoing HTTP connections can be signed to allow receivers to verify that the call came from the expected source. There was a proposol for a standardized way to add such signatures to HTTP calls, and the httpcaller implements this protocol. If you only use httpcaller to make calls to your own servers over an internal network this feature might not be necessary, but for calls that go over the internet it can be helpful to avoid spoofing.

If you do not set these options, the HTTP calls will not be signed. The signing key can also be set per call (see below).

DNS options

By default, the httpcaller uses the default DNS system resolver based on the settings from /etc/resolv.conf. But if you want to use a different DNS server, the following settings can be helpful:

In normal circumstances there is no need to change any of the DNS settings.

Blacklist/whitelist options

To prevent that httpcaller makes calls to forbidden webservers, you can install blacklists and/or whitelists.

By default, httpcaller is configured with the following blacklist: "192.168.0.0/16,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12". This means that all calls to the internal network are blocked, and that the application can only be used for calls to internet sites. If you use httpcaller for posting data to an internal endpoint, this must be overridden in the config file.

Other options

To be documented

JSON message format

The messages consumed from RabbitMQ must be JSON encoded, and must have a (minimal) form similar to this:

{
  "url": "http://www.example.com",
  "data": {
    "field1": "value1",
    "field2": "value2"
  }
}

The above instruction will be turned into a HTTP POST call to http://www.example.com with the supplied data as message body (converted to a "application/x-www-form-urlencoded" encoded message body). Alternatively, the data can also be supplied as string. But then you also pass a content-type as that cannot be auto-detected:

{
  "url": "http://www.example.com",
  "data": "textual post data",
  "content-type": "text/plain"
}

If you want to pass additional headers with the POST call, you can add that to the JSON as well:

{
  "url": "http://www.example.com",
  "data": "textual post data",
  "content-type": "text/plain",
  "headers": {
    "x-my-extra-header": "my-value"
  }
}

The config file can be used to restrict the number of outgoing HTTP connections, but this can be overridden on a per-message basis:

{
  "url": "http://www.example.com",
  "data": "textual post data",
  "content-type": "text/plain",
  "connections": 10
}

If you want to sign outgoing requests (see above), you can also override the settings from the config file:

{
  "url": "http://www.example.com",
  "data": "textual post data",
  "content-type": "text/plain",
  "sign": {
    "id": "the key id",
    "key": "private key in PEM format"
  }
}

JSON result format

HttpCallers posts the results of all calls back to RabbitMQ where you can inspect them. For each result, the original message is posted with an extra "results" array, holding one object for each attempt:

{
  "url": "http://www.example.com",
  "data": "textual post data",
  "content-type": "text/plain",
  "results": [{
    "ip": "1.2.3.4",
    "port": 80,
    "code": 200,
    "time": 1692108936,
    "duration": 0.210,
    "error": "optional error message",
    "header": "full HTTP response header"
  }]
}