# Scripting

> Usa Scripting de Proxyman, un proxy de depuración HTTP, para modificar solicitudes y respuestas con JavaScript, simular APIs (mock) y registrar mensajes WebSocket con onWebsocket.

## 1. ¿Qué es? {#1-whats-it}

Proxyman incluye una herramienta Scripting. Puedes escribir código JavaScript para cambiar la solicitud y la respuesta de forma flexible.

## 2. Beneficios {#2-benefits}

* Implementa Map Local / Map Remote / Breakpoint con código JS. **100 veces más rápido**
* Cambia el **Contenido de la solicitud**, incluidos Domain, Host, Scheme, Port, Path, método HTTP, encabezados HTTP, Query y Body (Encoded-Form, JSON, texto plano)
* Registra e inspecciona mensajes WebSocket con `onWebsocket` (**Proxyman 26.1.0 o posterior para macOS, Windows y Linux**).
* [Edita, descarta o envía mensajes WebSocket](https://proxyman.com/docs/es/scripting/modify-websocket-messages) con `onWebsocket` (**Proxyman 26.2.0 o posterior para macOS, y 26.1.0 o posterior para Windows/Linux**).
* Cambia el **Contenido de la respuesta**, incluidos el código de estado HTTP, los encabezados HTTP y el Body (JSON, Encoded-Form, texto plano, binario...)
* Incluye numerosos addons y librerías para tareas habituales, como hashing, codificación y decodificación, transformación JSON-Text, Beautify, ...
* **Escribe tus propios addons o librerías de JS**
* Diseñado para reemplazar la herramienta Rewrite GUI de Charles Proxy
* Comparte estado entre scripts de la sesión actual con [estado compartido o variables de entorno](https://proxyman.com/docs/es/scripting/environment-variables)

![Editor de scripts de Proxyman con una regla POST, Request y Response marcadas, Run as Mock API, y código de onRequest y onResponse](https://proxyman.com/docs/assets/proxyman-script-editor.jpg)

_Herramienta Scripting_

- [Fragmentos de código](https://proxyman.com/docs/es/scripting/snippet-code)

## 3. ¿Cómo usarlo? {#3-how-to-use-it}

Puedes acceder a la herramienta Scripting de estas formas:

* Menú **Scripting** -> **Script List…** (⌥⌘I)
* Haz clic derecho en un flujo -> **Tools** -> **Scripting…**

Consulta la página [Fragmentos de código](https://proxyman.com/docs/es/scripting/snippet-code) para ver una colección de fragmentos JS para la herramienta Scripting.

## 4. Scripting con solicitudes GraphQL {#4-scripting-with-graphql-requests}

Desde Proxyman 2.27.0 o posterior, la herramienta Scripting puede identificar una solicitud GraphQL por su QueryName. Consulta el siguiente documento sobre GraphQL.

- [GraphQL](https://proxyman.com/docs/es/advanced-features/graphql)

## 4.1 Scripting con WebSocket {#41-scripting-with-websocket}

Desde Proxyman 6.2.0 para macOS, usa `onRequest` y `onResponse` para modificar la URL de la solicitud WebSocket y los encabezados del handshake. Consulta [Cambiar la URL y los encabezados de WebSocket](https://proxyman.com/docs/es/scripting/snippet-code#change-websocket-url-requestsresponse-header).

### Registrar mensajes con onWebsocket {#log-messages-with-onwebsocket}

> **Aviso:** `onWebsocket` está disponible en **Proxyman 26.1.0 o posterior para macOS, Windows y Linux**. No está disponible en iOS.

Usa `onWebsocket(context, url, request, response, message)` para inspeccionar y registrar los mensajes capturados, tanto del cliente como del servidor:

```javascript
function onWebsocket(context, url, request, response, message) {
  console.log(`[${message.from}] ${message.type}: ${message.data}`);
}
```

De forma predeterminada, este hook es **de solo lectura**. Proxyman reenvía el mensaje antes de ejecutar el script. Cambiar sus argumentos o devolver un valor no modifica el tráfico ni los mensajes capturados. No hace falta ninguna sentencia `return`.

Para editar, descartar o enviar mensajes, marca **Modify WebSocket Messages** en la regla (macOS Proxyman 26.2.0 o posterior, Windows/Linux Proxyman 26.1.0 o posterior). Consulta [Modificar mensajes WebSocket](https://proxyman.com/docs/es/scripting/modify-websocket-messages).

**Cómo usarlo**

1. Captura una conexión WebSocket. Para `wss://`, activa SSL Proxying en el host para que Proxyman pueda leer sus mensajes.
2. Haz clic derecho en el flujo capturado y selecciona **Tools → Scripting**. El script generado incluye `onWebsocket` debajo de `onResponse`.
3. Añade tu código de registro y haz clic en **Save & Activate**.
4. Vuelve a conectar el WebSocket, envía mensajes y revisa el **Console Log** del script.

La regla debe estar activa cuando se abre la conexión. Vuelve a conectar después de crear una regla o de añadir `onWebsocket` a un script que no lo tenía cuando se abrió la conexión. La plantilla habitual de un script nuevo no incluye este hook, pero puedes añadirlo manualmente.

**Argumentos**

Mantén los cinco argumentos en orden. `context` y `url` describen el flujo y la regla que coincide. `request` y `response` contienen el handshake final, incluidos los cambios hechos por `onRequest`, `onResponse` o Breakpoint. `message` contiene:

| Campo | Valor |
| --- | --- |
| `message.type` | `"text"`, `"binary"`, `"ping"`, `"pong"` o `"close"`. |
| `message.from` | `"client"` o `"server"`. |
| `message.data` | Siempre es una cadena. Los datos binarios se muestran como texto UTF-8 cuando es posible; si no, como `Binary (length=N)`. No se aplican los decodificadores opcionales de la pestaña WebSocket. |
| `message.rawData` | Una copia de `Uint8Array`, disponible solo para mensajes binarios. Regístralo de forma explícita: no se incluye al enumerar `message`. |
| `message.closeCode` | Un número, disponible solo para mensajes de cierre, como `1000`. |

**Notas**

* Los frames de texto y binarios fragmentados se reensamblan antes de ejecutar el hook. Recibes un único mensaje completo, no una llamada por cada frame de continuación.
* Los payloads de ping/pong y los motivos de cierre no se exponen.
* Se admiten hooks síncronos y `async`, así como funciones auxiliares como `console.log`, `require`, `sharedState` y `$http`.
* Los cambios en un hook existente y el estado de activación de reglas, carpetas y herramientas se comprueban como máximo una vez por segundo, a medida que llegan mensajes. Las llamadas que ya están en cola pueden terminar.
* El Console Log conserva las últimas 200 entradas. Si los scripts no dan abasto, las llamadas a los hooks pueden omitirse con una advertencia mientras el tráfico continúa. Úsalo para depurar, no como archivo completo del tráfico.

Consulta [Fragmentos de código de mensajes WebSocket](https://proxyman.com/docs/es/scripting/snippet-code#log-websocket-messages-with-onwebsocket) para ver ejemplos de texto, JSON, binarios, ping/pong y cierre.

## 5. Ejemplos {#5-examples}

La siguiente guía muestra cómo escribir código JS para cambiar el dominio de la solicitud de producción a localhost y modificar el cuerpo de la respuesta.

1. Asegúrate de activar SSL Proxying para este dominio antes de crear el script.
2. Abre la herramienta Scripting y crea una nueva entrada de script (⌘N). También puedes hacer clic derecho en la solicitud -> Tools -> Scripting… => Proxyman crea un script por ti.
3. Ponle un nombre y define una Matching Rule.
4. Ejemplo: **Name**=Prueba en endpoint localhost, **URL**=https://proxyman.io
5. Activa las casillas **Request** y **Response** en **Run Script on**
6. Empieza a escribir código JS para la función `onRequest`

**onRequest(context, url, request) {}**

```javascript
// Import UUID addons
const { uuidv4 } = require("@addons/UUID.js");

function onRequest(context, url, request) {
  // print log
  console.log(request);

  // Change Production domain -> Localhost
  request.method = "GET";
  request.scheme = "http";
  request.host = "localhost";
  request.port = 8000;
  
  // Add new header
  request.headers["X-New-Header"] = "Hello From Scripting feature";
  request.headers["UUID"] = uuidv4(); // generate random UUIDv4
  delete request.headers["Key-Need-Delete"];

  // Update or Add a new Query
  request.queries["name"] = "Proxyman";

  // Update Body
  var body = request.body;
  body["name"] = "Proxyman";
  request.body = body;
  
  // Or Map the body with a local file (Proxyman 2.25.0+)
  // request.bodyFilePath = "~/Desktop/mockdata.json";
  
  // Done
  return request;
}
```

## Formato del objeto de onRequest() {#onrequest-object-format}

Los objetos `context`, `url` y `request` se definen así:

```javascript
// context (readonly)
{
    "scriptName": "<String> Your Script Name",
    "matchingRule": "<String> Your Matching Rule",
    "matchingMethod": "<String> Method",
    "isEnableOnRequest": "Bool",
    "isEnableOnResponse": "Bool",
    "filePath": "<String> Script path",
    "flow": { // Available from 2.16.0+
        "serverPort": "443",
        "serverIpAddress": "104.18.230.83",
        "clientIpAddress": "192.168.0.102",
        "remoteDeviceName": "iPhone XR",
        "remoteDeviceIP": "192.168.0.102",
        "id": "51",
        "clientPath": null,
        "clientPort": "51494",
        "clientName": null,
        "mapRemoteOriginalURL": "<String> Original URL before Map Remote modification (nullable)"
    },
}

// url (readonly)
url: String // => Present the full URL

// request
{
    "method": "<String> HTTP Method. Accept string method. Ex: GET, POST, ...",
    "scheme": "<String> Accept http or https",
    "host": "<String> Host of the request. Ex: api.proxyman.io, localhost, ...",
    "path": "<String>: Path of the URL. Ex: /v1/data",
    "port": "<Int> Accept int port number. Ex: 443, 8080, ..",
    "queries": "<[String: Any]> A JS Object (Dictionary) contains key values of the query",
    "headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
    "body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, Plain Text or Uint8Array",
    "bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
    "rawBody": "<Readonly>: A raw body String or Uint8Array",
    "preserveHostHeader": "<Bool> Preserve the Host",
    "isURLEncoding": "<Bool> Determine if Proxyman will perform URLEncoding when constructing the final URL. Default is True",
    "upstream": "<Object><Optional> Upstream TLS ALPN and HTTP/2 settings. Proxyman v26.0.0 or later for macOS."
}

```

> **Nota:** Puedes cambiar cualquier valor del objeto request excepto `rawBody`

Define `request.upstream` en `onRequest`. Consulta [Configuración de ALPN y HTTP/2 upstream](https://proxyman.com/docs/es/scripting/snippet-code#upstream-alpn-and-http2-settings) para ver un ejemplo y los campos compatibles.

> **Nota:** Si el **body** tiene un formato no válido por un encabezado Content-Type incorrecto, usa **rawBody** y analiza la cadena manualmente.

El tipo de `request.body` depende del encabezado Content-Type.

| Encabezado Content-Type | request.body |
| --- | --- |
| application/json o familias JSON | Objeto de JavaScript |
| application/x-www-form-urlencoded | Objeto de JavaScript |
| Texto plano o Content-Type basado en texto, p. ej., application/js, text/css, text/html, ... | String |
| El resto: datos binarios (application/zip, application/octet-stream) | Uint8Array |

Consulta el código JS común en la página Fragmentos de código:

- [Fragmentos de código](https://proxyman.com/docs/es/scripting/snippet-code)

7\. Empieza a escribir código en la función `onResponse`

```javascript
function onResponse(context, url, request, response) {
  console.log(response);

  // Update or Add a new header
  response.headers["Content-Type"] = "application/json";

  // Update status Code
  response.statusCode = 500;

  // Update Body
  var body = response.body;
  body["new-key"] = "Proxyman";
  response.body = body;

  // Or Map the body with a local file (Proxyman 2.25.0+)
  // response.bodyFilePath = "~/Desktop/mockdata.json";
  
  // Done
  return response;
}
```

## Formato de los objetos de onResponse() {#onresponse-objects-format}

Los objetos `context`, `url`, `request` y `response` se definen así:

```javascript
// context (readonly): Same with onRequest

// url (readonly): Same with onRequest

// request (readonly): Same with onRequest

// response
{
    "statusCode": "<Int> Status Code. Ex: 200, 400, 404,...",
    "httpVersion": "<String><Readonly> The HTTP Version",
    "statusPhrase": "<String><Readonly> HTTP Status Phrase. Ex Not Found, OK, ...",
    "headers": "<[String: Any]> A JS Object (Dictionary) contains key values of the header",
    "body": "Depend on the Content-Type header. It might be a dictionary for JSON and form, PlainText or Uint8Array",
    "rawBody": "<Readonly>: A raw body String or Uint8Array",
    "bodyFilePath": "<String><Optional> Set a body with a local file. See example in Snippet Code Page"
}
```

> **Nota:** Puedes cambiar `statusCode`, `headers` y `body` del objeto response.

> **Nota:** Si el **body** tiene un formato no válido por un encabezado Content-Type incorrecto, usa **rawBody** y analiza la cadena manualmente.

El tipo de `response.body` depende del encabezado Content-Type.

| Encabezado Content-Type | response.body |
| --- | --- |
| application/json o familias JSON | Objeto de JavaScript |
| application/x-www-form-urlencoded | Objeto de JavaScript |
| Texto plano o Content-Type basado en texto, p. ej., application/js, text/css, text/html, ... | String |
| El resto (application/zip, application/octet-stream) | Uint8Array |

> **Nota:** Debes devolver los objetos `request` y `response` en las funciones `onRequest` y `onResponse`.

## 6. Addons y librerías integradas {#6-built-in-addons-and-libraries}

Proxyman incluye numerosos addons y librerías para tareas habituales: hashing, codificación y decodificación, ...

- [Addons](https://proxyman.com/docs/es/scripting/addons)

- [Librerías JS integradas](https://proxyman.com/docs/es/scripting/built-in-js-libraries)

- [Fragmentos de código](https://proxyman.com/docs/es/scripting/snippet-code)

## 7. Depurar errores de JS {#7-debugging-js-error}

A veces aparecen errores de JavaScript por errores de sintaxis, código no válido, etc. Puedes depurarlos leyendo los mensajes de error en la consola o usando `console.log()`.

## 8. Usar Scripting como API simulada (mock) {#8-use-scripting-as-a-mock-api}

Desde Proxyman 2.32.0, puedes usar la herramienta Scripting como API simulada (mock). Tu solicitud nunca llega al servidor y tú defines el cuerpo de la respuesta. Funciona igual que Map Local.

Es útil cuando la API REST real todavía no está disponible. Puedes definirla y probarla en local.

Para activar la API simulada:

1. Abre la herramienta Scripting -> selecciona el script
2. Marca la casilla **Run as Mock API**, junto a las casillas **Request** y **Response** del editor de scripts (consulta la captura del editor de scripts más arriba).

Después, puedes definir una respuesta como de costumbre:

```javascript
function onResponse(context, url, request, response) {

  // Init new body
  var body = {};
  body["new-key"] = "Proxyman";
  response.body = body;

  // Or map from a file
  // response.bodyFilePath = "~/Desktop/myfile.json"
  
  // Done
  return response;
}
```

## 9. Notas {#9-notes}

* Debes devolver los objetos `request` y `response` en las funciones `onRequest` y `onResponse`.
* **Proxyman 4.16.0 o posterior**: Proxyman ahora convierte los datos binarios a **Uint8Array**.
* Proxyman 4.15.0 o anterior: JavaScript no tiene un tipo Data, así que un body binario se convierte en una **cadena codificada en Base64** en JavaScript. Para pasar un Uint8Array, blob o ArrayBuffer al body, primero conviértelo en una **cadena codificada en Base64**.

## Más información {#learn-more}

* [¿Cómo mapear una respuesta local para solicitudes GraphQL?](https://proxyman.com/posts/2021-01-24-How-To-Map_Response_For_GraphQL_Requests)
* [Cómo saltarse la política CORS](https://proxyman.com/posts/2020-08-31-How-to-by-pass-CORS-policy)
