# Como testar uma origem com Functions

import DocButton from '~/components/webkit/DocButton.vue';

Testar novo software com dados sintéticos frequentemente perde padrões de uso do mundo real. Functions executando no Firewall podem espelhar tráfego de produção para uma origem de teste, permitindo validar o comportamento do novo software com requisições reais de usuários—sem afetar a experiência do usuário.

## Como funciona

Functions no listener do Firewall podem executar tarefas sem impactar as requisições dos usuários. Quando uma requisição chega ao Firewall, a função cria uma requisição duplicada e a envia para sua origem de teste, enquanto a requisição original continua para sua origem de produção. Isso acontece de forma assíncrona, sem adicionar latência às requisições dos usuários.

Benefícios principais:

- **Validação com tráfego real**: Teste com dados reais de usuários, incluindo casos de borda que testes sintéticos perdem
- **Zero impacto ao usuário**: Requisições completam normalmente enquanto dados de teste são coletados
- **Teste em escala de produção**: Verifique se sua nova origem suporta a carga de produção
- **Monitoramento configurável**: Registre respostas, erros e métricas de latência

---

## Pré-requisitos

Antes de começar, certifique-se de ter:

- Uma [application](/pt-br/documentacao/produtos/guias/build/criar-uma-aplicacao) com tráfego de produção
- Um [domínio](/pt-br/documentacao/produtos/guias/configurar-dominio/) associado à sua aplicação
- Uma origem de teste (seu novo software) acessível via HTTPS
- Um [Firewall](/pt-br/documentacao/produtos/secure/firewall/) associado ao seu domínio

---

## Criando a função de espelhamento de tráfego

### Passo 1: Criar uma nova função

1. Acesse o [Azion Console](/pt-br/documentacao/produtos/guias/conhecendo-o-azion-console/) > **Functions**.
2. Clique em **+ Function**.
3. Nomeie sua função (ex: `Traffic Mirroring`).
4. Na aba **Code**, adicione o seguinte código:

```javascript
const TEST_DOMAIN = "www.your-test-origin.com";

async function firewallHandler(event) {
  const originalUrl = new URL(event.request.url);
  const testUrl = `${originalUrl.protocol}//${TEST_DOMAIN}${originalUrl.pathname}${originalUrl.search}`;

  let fetchOptions = {
    method: event.request.method,
    headers: Object.fromEntries(event.request.headers)
  };

  if (event.request.body) {
    fetchOptions["body"] = await event.request.text();
  }

  event.waitUntil(fetch(testUrl, fetchOptions));
  event.continue();
}

addEventListener("firewall", (event) => event.waitUntil(firewallHandler(event)));
```

5. Substitua `www.your-test-origin.com` pelo domínio da sua origem de teste.
6. Clique em **Save**.

:::note
Este código usa `event.waitUntil()` em dois níveis, cada um com uma responsabilidade distinta:

- **Nível externo** (`addEventListener`): O `event.waitUntil()` envolvendo `firewallHandler(event)` garante que o runtime aguarde a conclusão do handler assíncrono antes de considerar o evento finalizado.
- **Nível interno** (dentro do handler): O `event.waitUntil(fetch(...))` garante que o fetch para sua origem de teste execute sem bloquear a requisição do usuário. Combinado com o `event.continue()` imediato, isso torna a função "transparente" para o usuário—zero latência adicionada.

O método `event.continue()` é chamado imediatamente após `event.waitUntil(fetch(...))`, permitindo que a requisição original prossiga para sua origem de produção enquanto o fetch para a origem de teste acontece assincronamente em segundo plano.
:::

### Passo 2: Configurar a função no Firewall

:::note
Os passos a seguir descrevem a configuração no Azion Console. Para informações detalhadas sobre essas interfaces, consulte a [referência de Functions](/pt-br/documentacao/produtos/build/applications/functions/) e a [documentação do Firewall](/pt-br/documentacao/produtos/secure/firewall/).
:::

1. Acesse o [Azion Console](/pt-br/documentacao/produtos/guias/conhecendo-o-azion-console/) > **Firewall**.
2. Selecione o firewall associado ao seu domínio.
3. Habilite o módulo **Functions** se ainda não estiver habilitado.
4. Vá para a aba **Functions Instances**.
5. Clique em **+ Function Instance**.
6. Nomeie sua instância (ex: `Traffic Mirroring Instance`).
7. Selecione a função `Traffic Mirroring`.
8. Clique em **Save**.

### Passo 3: Criar uma regra para disparar a função

1. No mesmo firewall, vá para a aba **Rules Engine**.
2. Clique em **+ Rule**.
3. Nomeie sua regra (ex: `Mirror Traffic to Test Origin`).
4. Em **Criteria**, configure quando espelhar o tráfego:
   - **If** `${uri}` **matches regex** `.*` (espelha todas as requisições)
   - Ou especifique caminhos: **If** `${uri}` **starts with** `/api` (espelha apenas requisições de API)
5. Em **Behaviors**, selecione **Run Function**.
6. Escolha a `Traffic Mirroring Instance`.
7. Clique em **Save**.

Aguarde alguns minutos para a propagação. Sua função agora espelha o tráfego de produção para sua origem de teste.

---

## Adicionando monitoramento com Real-Time Events

:::note
Os recursos descritos nesta seção—`event.console.log()`, `event.console.warn()`, e integração com Real-Time Events—são capacidades da plataforma Azion documentadas separadamente. Para mais informações, consulte [Real-Time Events](/pt-br/documentacao/produtos/observe/real-time-events/).
:::

Para entender como sua origem de teste responde, adicione logging que aparece no Real-Time Events.

### Passo 4: Atualizar a função com logging

Substitua o código da sua função por:

```javascript
const TEST_DOMAIN = "www.your-test-origin.com";

async function firewallHandler(event) {
  try {
    const originalUrl = new URL(event.request.url);
    const testUrl = `${originalUrl.protocol}//${TEST_DOMAIN}${originalUrl.pathname}${originalUrl.search}`;

    let fetchOptions = {
      method: event.request.method,
      headers: Object.fromEntries(event.request.headers),
      signal: AbortSignal.timeout(5000) // Timeout menor para fases de diagnóstico; aumente se sua origem de teste for lenta. A versão reutilizável no Passo 6 usa 10000 ms como padrão.
    };

    if (event.request.body) {
      fetchOptions["body"] = await event.request.text();
    }

    const startTime = Date.now();
    const testOriginResponse = await fetch(testUrl, fetchOptions);
    const responseTime = (Date.now() - startTime) / 1000;

    event.console.log(`[${testOriginResponse.status}, ${responseTime}s]`);

    if (testOriginResponse.status > 399) {
      // Nota: esta estrutura de log estende os campos canônicos da
      // referência fonte com request_path e response_time
      // para contexto adicional de observabilidade.
      event.console.warn(JSON.stringify({
        request_method: event.request.method,
        request_body: fetchOptions["body"],
        request_headers: fetchOptions["headers"],
        response_body: await testOriginResponse.text(),
        response_status: testOriginResponse.status,
        request_path: originalUrl.pathname,   // campo estendido
        response_time: responseTime           // campo estendido
      }));
    }
  } catch (err) {
    if (err.name === "TimeoutError") {
      event.console.warn("Test origin timeout");
    } else {
      event.console.warn(`Error: ${err.message}`);
    }
  }

  // event.continue() está FORA do try/catch intencionalmente.
  // Isso garante que a requisição do usuário sempre prossiga para a origem de produção,
  // independentemente de erros na comunicação com a origem de teste.
  event.continue();
}

addEventListener("firewall", (event) => event.waitUntil(firewallHandler(event)));
```

:::note
`AbortSignal.timeout()` é uma API Web padrão para definir timeouts de requisições. Para mais informações, consulte a [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static).
:::

Esta versão atualizada:

- Registra status de resposta e tempo para cada requisição
- Registra informações detalhadas para respostas de erro (4xx e 5xx)
- Implementa um timeout de 5 segundos para prevenir requisições de longa duração
- Trata timeouts e outros erros de forma elegante

:::caution
**Esta versão quebra o princípio de zero latência.**

Adicionar `await` ao fetch muda fundamentalmente o comportamento da função:

- **Antes (Passo 1)**: A função era "transparente"—`event.continue()` era chamado imediatamente, e o fetch acontecia assincronamente. Zero latência adicionada para os usuários.
- **Agora (Passo 4)**: A função se torna "bloqueante"—ela aguarda a resposta da origem de teste antes de chamar `event.continue()`. As requisições dos usuários são atrasadas pelo tempo de resposta da origem de teste.

Esta abordagem **abandona o princípio central** do espelhamento de tráfego descrito neste guia. Use-a apenas durante fases curtas de diagnóstico, nunca como padrão operacional. Para monitoramento estendido sem impacto ao usuário, considere usar [Data Stream](/pt-br/documentacao/produtos/observe/data-stream/).
:::

### Passo 5: Visualizar logs no Real-Time Events

1. Acesse o [Azion Console](/pt-br/documentacao/produtos/guias/conhecendo-o-azion-console/) > **Real-Time Events**.
2. Selecione **Functions** nas opções de filtro.
3. Filtre pelo nome da sua função ou firewall.
4. Observe os logs aparecendo conforme as requisições são processadas.

Você verá entradas como:

- `[200, 0.142s]` — Respostas bem-sucedidas com latência
- Logs de aviso com detalhes da requisição para respostas de erro
- Avisos de timeout se sua origem de teste for lenta

---

## Tornando a função reutilizável

Para múltiplos cenários de teste, use variáveis de ambiente e JSON Args em vez de valores fixos no código.

### Passo 6: Criar uma função reutilizável

Atualize o código da sua função:

```javascript
async function firewallHandler(event) {
  try {
    const testDomain = event.args.url || Azion.env.get("TEST_URL") || "www.default-test.com";
    const testTimeout = event.args.timeout || Azion.env.get("TEST_TIMEOUT") || 10000;

    const originalUrl = new URL(event.request.url);
    const testUrl = `${originalUrl.protocol}//${testDomain}${originalUrl.pathname}${originalUrl.search}`;

    let fetchOptions = {
      method: event.request.method,
      headers: Object.fromEntries(event.request.headers),
      signal: AbortSignal.timeout(testTimeout)
    };

    if (event.request.body) {
      fetchOptions["body"] = await event.request.text();
    }

    const startTime = Date.now();
    const testOriginResponse = await fetch(testUrl, fetchOptions);
    const responseTime = (Date.now() - startTime) / 1000;

    event.console.log(`[${testOriginResponse.status}, ${responseTime}s]`);

    if (testOriginResponse.status > 399) {
      // Nota: esta estrutura de log estende os campos canônicos da
      // referência fonte com request_path e response_time
      // para contexto adicional de observabilidade.
      event.console.warn(JSON.stringify({
        request_method: event.request.method,
        request_body: fetchOptions["body"],
        request_headers: fetchOptions["headers"],
        response_body: await testOriginResponse.text(),
        response_status: testOriginResponse.status,
        request_path: originalUrl.pathname,   // campo estendido
        response_time: responseTime           // campo estendido
      }));
    }
  } catch (err) {
    if (err.name === "TimeoutError") {
      event.console.warn("Test origin timeout");
    } else {
      event.console.warn(`Error: ${err.message}`);
    }
  }

  // event.continue() está FORA do try/catch intencionalmente.
  // Isso garante que a requisição do usuário sempre prossiga para a origem de produção,
  // independentemente de erros na comunicação com a origem de teste.
  event.continue();
}

addEventListener("firewall", (event) => event.waitUntil(firewallHandler(event)));
```

:::caution
Assim como a versão no Passo 4, esta função usa `await` na chamada fetch e, portanto, é bloqueante. As requisições dos usuários serão atrasadas pelo tempo de resposta da origem de teste. De acordo com benchmarks, esta abordagem pode tornar a função até 14x mais lenta que a versão de zero latência no Passo 1, dependendo do tempo de resposta da sua origem de teste.

Use esta configuração apenas durante fases ativas de teste.
:::

Agora você pode configurar a função através de:

- **JSON Args**: Adicione `{"url": "www.test-origin.com", "timeout": 3000}` na instância da função
- **Variáveis de ambiente**: Defina `TEST_URL` e `TEST_TIMEOUT` no ambiente da sua função

Isso permite criar múltiplas instâncias da função com diferentes origens de teste sem modificar o código.

:::note
O valor padrão do timeout é 10000 ms (10 segundos). Ajuste este valor com base no tempo de resposta esperado da sua origem de teste. Timeouts menores falham mais rápido, mas podem perder respostas válidas de origens mais lentas.
:::

---

## Analisando os resultados do teste

Após executar o espelhamento de tráfego, analise o comportamento da sua origem de teste:

1. **Tempos de resposta**: Compare a latência entre as origens de produção e teste
2. **Taxas de erro**: Verifique respostas 4xx e 5xx nos logs do Real-Time Events
3. **Frequência de timeout**: Monitore com que frequência as requisições excedem seu limite de timeout
4. **Padrões de requisição**: Verifique se sua origem de teste trata todos os tipos de requisição (GET, POST, PUT, DELETE)

Quando sua origem de teste tratar o tráfego de produção com sucesso, com latência e taxas de erro aceitáveis, ela está pronta para implantação em produção.

---

## Recursos relacionados

- [Referência de Functions](/pt-br/documentacao/produtos/build/applications/functions/)
- [Rules Engine para Firewall](/pt-br/documentacao/produtos/secure/firewall/rules-engine/)
- [Real-Time Events](/pt-br/documentacao/produtos/observe/real-time-events/)