# Functions for Firewall

A function on a [firewall](/en/documentation/platform/firewall/) is JavaScript code that the firewall runs on a request when one of its rules calls it. The code receives the request as a `firewall` event. The methods of that event decide the request: they add headers and let it continue, deny it, drop it, or answer it. To follow a request from the rule to the function and back, refer to [How Firewall works](/en/documentation/platform/firewall/how-it-works/).

A function on a firewall runs before the application, so its code decides whether a request reaches the application at all. Code that takes part in serving a request the firewall allowed runs on an application instead, from a function whose execution environment is `application`. For the instance that puts such a function on an application, refer to [Function instances](/en/documentation/platform/applications/functions-instances/).

---

## Execution environment

Every function carries an execution environment, and a firewall runs only a function whose environment is `firewall`. You set the environment when you create the function in [Functions](/en/documentation/platform/functions/):

| Interface | Setting                                                      | Value for a firewall function |
| --------- | ------------------------------------------------------------ | ----------------------------- |
| API       | `execution_environment`, a field of the function             | `firewall`                    |
| Azion CLI | `--execution-environment`, a flag of `azion create function` | `firewall`                    |

The API field takes `application` or `firewall`, and it defaults to `application`. A function created without the field is an application function, and the API refuses an instance of it on a firewall. The CLI help reads `Either 'edge_application' or 'edge_firewall'` for the flag. Neither value is in the API enum, and the API refuses both. `azion describe function --function-id <function-id> --format json` prints the field of an existing function, such as `"execution_environment": "application"`.

In Azion Console, the **Function** field of a function instance lists only the functions whose environment is `firewall`.

A function with the `firewall` environment still runs on no request until two more objects point at it. A [function instance](/en/documentation/platform/firewall/functions-instances/) puts the function on one firewall, and a rule whose *Run Function* behavior names that instance calls it. To write that rule, refer to [Run Function](/en/documentation/platform/firewall/rules-engine/#run-function).

The firewall must also have Functions enabled. Until it does, Azion Console hides the firewall's **Functions Instances** tab and lists the behavior as *Run Function - required Functions*, which you cannot select. The **Functions** switch sits in the **Modules** section of the firewall's **Main Settings** tab. A firewall created through the API, the CLI, or the Console create page starts with Functions enabled. The Console create drawer starts with it off.

---

## The firewall event

A function on a firewall registers a listener for the `firewall` event with `addEventListener`. The listener receives the event, which carries the request in `event.request` and the methods that decide it.

To read a header the request already carries, call `event.request.headers.get(<name>)`. The [Functions on a firewall](/en/documentation/platform/functions/general-firewall-example/) example reads several request headers this way to choose what its handler does.

Every function on a firewall must end with a finishing outcome, such as `event.continue()`, `event.deny()`, or `event.drop()`. To write conditionals that always reach one outcome, refer to [Firewall best practices](/en/documentation/platform/firewall/best-practices/).

---

## Event methods

The table lists the methods a function on a firewall calls on the `firewall` event, in alphabetical order.

| Method                      | Arguments                                             | What it does                                                                                                                          |
| --------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `event.addRequestHeader()`  | A header name and its value                           | Adds the header to the request that goes on to the origin                                                                             |
| `event.addResponseHeader()` | A header name and its value                           | Adds the header to the response the user receives                                                                                     |
| `event.args()`              | The name of an argument, such as `'arg_name'`         | Reads that argument from the **Arguments** of the function instance                                                                   |
| `event.continue()`          | None                                                  | Ends the function with a finishing outcome. The firewall resumes processing from the *Run Function* behavior that called the function |
| `event.deny()`              | None                                                  | Finishes the request with HTTP `403 Forbidden`. The status, the body, and the reason are fixed                                        |
| `event.drop()`              | None                                                  | Finishes the request without an answer to the client                                                                                  |
| `event.respondWith()`       | A `Response` object                                   | Intercepts the request and answers it with a custom response, whose status, headers, and content the object sets                      |
| `event.waitUntil()`         | A promise, such as the one an `async` handler returns | Runs an `async` handler from the synchronous listener. Without it, the promise can end in unexpected exceptions                       |

### event.addRequestHeader

`event.addRequestHeader()` adds a header to the request that goes on to the origin. It takes two arguments, the header name and its value. This listener adds two headers and lets the request continue:

```javascript
  addEventListener("firewall", (event) => {
      event.addRequestHeader("X-Custom-Header-1", "1");
      event.addRequestHeader("X-Custom-Header-2", "2");
      event.continue();
  });
```

### event.addResponseHeader

`event.addResponseHeader()` adds a header to the response the user receives. It takes the same two arguments, the header name and its value. This listener adds two response headers and lets the request continue:

```javascript
  addEventListener("firewall", (event) => {
      event.addResponseHeader("X-Custom-Header-3", "3");
      event.addResponseHeader("X-Custom-Header-4", "4");
      event.continue();
  });
```

### event.deny

`event.deny()` finishes the request with HTTP `403 Forbidden`. It takes no arguments, so the function cannot change the status, the body, or the reason of that response. For a response the function controls, `event.respondWith()` is the method to call. This listener denies every request it receives:

```javascript
  addEventListener("firewall", (event) => {
      event.deny();
  });
```

### event.drop

`event.drop()` finishes the request without an answer to the client, and takes no arguments. This listener drops every request it receives:

```javascript
  addEventListener("firewall", (event) => {
      event.drop();
  });
```

### event.respondWith

`event.respondWith()` intercepts the request and answers it with a custom response. It takes a `Response` object, which sets the status, the headers, and the content of the answer. The call in this section runs inside a `firewall` listener and answers with a JSON body, the status `599`, and a `content-type` of `application/json`:

```javascript
    event.respondWith(new Response('{"my_custom_response": true}', {
        status: 599,
        headers: { "content-type": "application/json" }
    }));
```

---

## Metadata

A function on a firewall reads request metadata to filter access to the application and to apply different logic by scenario. Four groups of that metadata describe where a request comes from and how it arrives. The [Metadata API](/en/documentation/devtools/runtime/api-reference/metadata/) reference lists the fields of each group.

| Group  | What it holds                                                                              | Fields                                                                               |
| ------ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| GeoIP  | Where the request comes from. Use it to deny access to the application from certain places | [GeoIP metadata](/en/documentation/devtools/runtime/api-reference/metadata/#geoip)   |
| Remote | The IP address and the TCP port the client uses                                            | [Remote metadata](/en/documentation/devtools/runtime/api-reference/metadata/#remote) |
| Server | The protocol the request uses                                                              | [Server metadata](/en/documentation/devtools/runtime/api-reference/metadata/#server) |
| TLS    | Details available when the request arrives over a secure TLS connection                    | [TLS metadata](/en/documentation/devtools/runtime/api-reference/metadata/#tls)       |

---

## Limits

A function on a firewall runs within two sets of bounds. The limits of the function itself, such as its code size, memory, CPU time, and sub-requests, are on [Functions limits](/en/documentation/platform/functions/limits/). The bounds of the firewall and of its function instances are on [Firewall limits](/en/documentation/platform/firewall/limits/).

The arguments of a function instance, which the function reads with `event.args()`, hold at most 100,000 bytes. A larger payload is refused with `Value size (in bytes) is too big. Maximum size allowed is 100000 bytes.`

---

## Errors

Two refusals come from the execution environment of a function. Azion CLI prints each message inside brackets, after `Error: Failed to create function:` or `Error: failed to create the Firewall Function Instance:`. `azion create function` closes its message with `Check your settings and try again. If the error persists, contact Azion support`.

| Message                                                                                | Command                          | What causes it                                                                                  | What to do                                                       |
| -------------------------------------------------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| `"edge_firewall" is not a valid choice.`                                               | `azion create function`          | `--execution-environment edge_firewall`, a value the CLI help lists and the API does not accept | Use `--execution-environment firewall`                           |
| `Invalid edge function runtime. You should use a function designed for Edge Firewall.` | `azion create firewall-instance` | The function's execution environment is `application`                                           | Instantiate a function whose execution environment is `firewall` |

---

## Related resources

- [Run a function on a firewall](/en/documentation/guides/application-development/functions-and-runtime/firewall.md): The procedure that creates a function, instantiates it on a firewall, and calls it from a rule in Azion Console.
- [Instantiate a function on a firewall](/en/documentation/guides/application-security/firewall-and-waf/instantiate-functions.md): The procedure that creates a function instance on a firewall from the Azion API.
- [JavaScript examples](/en/documentation/platform/functions/javascript-examples.md#firewall): Complete functions that run on a firewall, to copy as a starting point.
- [Firewall best practices](/en/documentation/platform/firewall/best-practices.md): How to write conditionals and asynchronous code so that a firewall function always reaches its outcome.
