Writing a hooks handler for a new language

Dredd itself is written in JavaScript, so having hooks in JavaScript is native to it. Other languages need so-called hooks handlers.

Several hooks handlers already exist, either maintained by Dredd authors or external contributors. If you didn’t find your favorite language among them, at this place you can learn how to create a new hooks handler.

Note

Deserve eternal praise and contribute hooks handler for Java! See #875

What is a hooks handler?

Hooks handler is a process running separately from Dredd, usually started by Dredd as a child process when invoking Dredd with the --language option. When Dredd performs testing, it communicates with the hooks handler over TCP socket. The hooks handler runs hooks for each HTTP transaction and lets Dredd know whether something got modified.

Hooks handler life cycle

  1. Dredd starts the command given in the --language option as its child process (subprocess). Paths to files with hooks given in --hookfiles are resolved to absolute paths and given to the child process as arguments.

  2. The hooks handler reads paths to hooks from arguments and loads the hooks code.

  3. The hooks handler opens a TCP socket on localhost, port 61321.

  4. Dredd waits for a moment and then tries to connect to localhost, port 61321.

  5. For each type of hooks Dredd creates a message and sends it to the socket. The message contains UUID and serialized transaction object (or an array of them, in case of beforeAll, afterAll). Individual messages are sent as JSON documents separated by a newline.

  6. Hooks handler reads a message, calls a corresponding hook code, and sends back a message with modified contents.

  7. Dredd awaits a message with corresponding UUID. Once it arrives, Dredd overwrites its internal HTTP transaction data with the ones from the incoming message.

../_images/hooks-handler.png

Implementation guide

A hooks handler is a CLI command, which implements following:

  • It accepts paths to hook files as arguments. They are already passed resolved as absolute paths, in the right order.

  • It allows users to register hook functions in the hook files, i.e. it provides a hooks API similar to those in other hooks handler implementations (see JavaScript, Python, Ruby). It allows to register all types of hooks supported by Dredd.

  • It loads the hook files and registers any hook functions found in them for later execution.

  • It runs a TCP socket server on port 61321 and prints Starting to stdout when ready.

Handling hooks

When any data is received by the TCP server, the hooks handler:

  • Adds every received character to a buffer.

  • When the delimiter LINE FEED (LF) character encoded as UTF-8 (0A hex, \n in most languages) is received:

    • Parses the message in the buffer as JSON.

    • Finds the hook type in the event key of the received object and executes respective registered hook function(s). Beware, beforeEach and afterEach are overloaded - read the TCP socket message format carefully.

  • When a hook function is being executed:

    • Passes the value of the data key of the received object to the executed hook function.

    • Allows the hook function to modify the data.

  • When a hook function is done:

    • Takes the modified data and serializes it back to JSON with the same uuid as it has received

    • Sends the JSON back as a TCP message

    • Sends a LINE FEED (LF) character encoded as UTF-8 (0A hex, \n in most languages) as TCP message delimiter

TCP socket message format

  • transaction (object)

    • uuid: 234567-asdfghjkl (string) - ID used for unique identification of the message on both server and client sides

    • event: event (enum) - Hook type

      • beforeAll (string) - Signals the hooks handler to run the beforeAll hooks

      • beforeEach (string) - Signals the hooks handler to run the beforeEach and before hooks

      • beforeEachValidation (string) - Signals the hooks handler to run the beforeEachValidation and beforeValidation hooks

      • afterEach (string) - Signals the hooks handler to run the after and afterEach hooks

      • afterAll (string) - Signals the hooks handler to run the afterAll hooks

    • data (enum) - Data passed as an argument to the hook function

Termination

When there is an error or when the testing is done, Dredd signals the hooks handler process to terminate. This is done repeatedly with delays. When termination timeout is over, Dredd loses its patience and kills the process forcefully.

On Linux or macOS, Dredd uses the SIGTERM signal to tell the hooks handler process it should terminate. On Windows, where signals do not exist, Dredd sends the END OF TEXT character (03 hex, which is ASCII representation of Ctrl+C) to standard input of the process.

End-to-end test suite

There is a BDD test suite called dredd-hooks-template, which ensures that the public interface of each hooks handler works as Dredd expects. The test suite is written in Gherkin and uses Cucumber as a test runner.

https://raw.githubusercontent.com/apiaryio/dredd-hooks-template/master/passing.png

When developing a new hooks handler, make sure it passes the test suite. Third party hooks handlers not passing the test suite cannot be endorsed by Dredd maintainers, integrated with Dredd’s --language option, or added to Dredd’s documentation.

If you have any issues integrating the test suite to your project, reach out to the maintainers in Dredd issues, we’re happy to help!

Configuration options

There are several configuration options, which can help you during development of the hooks handler:

Warning

Behavior of the following options is currently broken (see #917) and it is recommended to stick to localhost and port 61321 until fixed:

Note

The options mention hooks worker in their names, but it stands for the same as hooks handler. There is a proposal to rename the options in the future: #1101

Need help? No problem!

If you have any questions, please:

  • Have a look at the reference Python and Ruby implementations.

  • If your language is compiled, check out how Go and Rust are done.

  • File an issue and get help from Dredd maintainers.