{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://www.anguskit.com/schema/angusmeter/plugins/angus-http.json",
  "title": "HTTP/HTTPS Step Configuration",
  "description": "Configuration for the angus-http protocol sampler plugin",
  "type": "object",
  "properties": {
    "method": {
      "type": "string",
      "enum": [
        "GET",
        "POST",
        "PUT",
        "DELETE",
        "PATCH",
        "HEAD",
        "OPTIONS"
      ],
      "description": "HTTP method to use for the request"
    },
    "url": {
      "type": "string",
      "format": "uri",
      "description": "Full request URL (may contain ${variable} placeholders). URL fragments (#...) are preserved after query-param injection."
    },
    "queryParams": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Query parameters appended to the URL (URL-encoded automatically)"
    },
    "headers": {
      "type": "object",
      "additionalProperties": {
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        ]
      },
      "description": "Request headers. A string value sends a single header; a string array sends the header once per array element (e.g. multi-value X-Forwarded-For). Header names are matched case-insensitively."
    },
    "body": {
      "type": "string",
      "description": "Raw request body content. For bodyType=binary this is interpreted as a file path or http(s):// URL (URL will be downloaded and cached once). Ignored for GET/HEAD."
    },
    "bodyType": {
      "type": "string",
      "enum": [
        "raw",
        "json",
        "xml",
        "form",
        "multipart",
        "binary"
      ],
      "default": "raw",
      "description": "Body content type. 'json'/'xml' auto-set Content-Type. 'form' sends application/x-www-form-urlencoded. 'multipart' sends multipart/form-data (streamed for large files). 'binary' streams the file at `body` as application/octet-stream."
    },
    "formFields": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Form fields for bodyType=form (application/x-www-form-urlencoded)"
    },
    "multipartFields": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Field name"
          },
          "value": {
            "type": "string",
            "description": "Field value (text) or file path or http(s):// URL (for type=file, URL will be downloaded and cached once)"
          },
          "type": {
            "type": "string",
            "enum": [
              "text",
              "file"
            ],
            "default": "text"
          },
          "contentType": {
            "type": "string",
            "description": "MIME type for this part"
          },
          "filename": {
            "type": "string",
            "description": "Override filename for file parts"
          }
        },
        "required": [
          "name",
          "value"
        ]
      },
      "description": "Multipart form fields for bodyType=multipart. For type=file, value can be a file path or http(s):// URL (URL will be downloaded and cached once). File parts are streamed to avoid OOM."
    },
    "timeout": {
      "type": "integer",
      "default": 30,
      "minimum": 1,
      "maximum": 300,
      "description": "Request timeout in seconds"
    },
    "connectTimeout": {
      "type": "integer",
      "default": 10,
      "minimum": 1,
      "maximum": 60,
      "description": "Connection timeout in seconds"
    },
    "followRedirects": {
      "type": "boolean",
      "default": true,
      "description": "Whether to follow HTTP redirects (3xx)"
    },
    "httpVersion": {
      "type": "string",
      "enum": [
        "1.1",
        "2",
        "auto"
      ],
      "default": "2",
      "description": "HTTP protocol version. '2' prefers HTTP/2 with transparent HTTP/1.1 fallback via ALPN; '1.1' forces HTTP/1.1; 'auto' uses the JDK default (currently equivalent to '2')."
    },
    "auth": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": [
            "NONE",
            "BASIC",
            "BEARER",
            "OAUTH2_CLIENT_CREDENTIALS",
            "OAUTH2_PASSWORD",
            "OAUTH2_REFRESH_TOKEN",
            "API_KEY",
            "DIGEST"
          ],
          "default": "NONE"
        },
        "username": {
          "type": "string"
        },
        "password": {
          "type": "string",
          "writeOnly": true
        },
        "token": {
          "type": "string",
          "writeOnly": true
        },
        "clientId": {
          "type": "string"
        },
        "clientSecret": {
          "type": "string",
          "writeOnly": true
        },
        "tokenUrl": {
          "type": "string",
          "format": "uri"
        },
        "scope": {
          "type": "string",
          "description": "OAuth2 scope, space-separated; applies to all OAUTH2_* grant types"
        },
        "refreshToken": {
          "type": "string",
          "description": "Required for OAUTH2_REFRESH_TOKEN grant",
          "writeOnly": true
        },
        "apiKey": {
          "type": "string",
          "writeOnly": true
        },
        "apiKeyHeader": {
          "type": "string",
          "default": "X-API-Key"
        },
        "scheme": {
          "type": "string",
          "description": "Override auth scheme (e.g. 'Token' instead of 'Bearer')"
        }
      },
      "description": "Authentication configuration. DIGEST performs a challenge-response round-trip: the sampler issues the request once, consumes the 401 WWW-Authenticate, and retries automatically."
    },
    "cookies": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Cookies to send with the request as name-value pairs. Merged into any existing 'Cookie' header."
    },
    "tls": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "description": "Explicit TLS switch (ignored for HTTPS URLs — TLS is always active for https:// scheme)."
        },
        "trustAll": {
          "type": "boolean",
          "default": false,
          "description": "Skip server certificate verification (for test environments only). A warning is emitted when enabled."
        },
        "verifyHostname": {
          "type": "boolean",
          "description": "If false, skips hostname verification against the server certificate CN/SANs. Defaults follow trustAll when unset."
        },
        "keystorePath": {
          "type": "string",
          "description": "Path to client keystore (PKCS12/JKS) for mTLS; store format is inferred from the extension."
        },
        "keystorePassword": {
          "type": "string",
          "description": "Client keystore password."
        },
        "truststorePath": {
          "type": "string",
          "description": "Path to custom trust store."
        },
        "truststorePassword": {
          "type": "string",
          "description": "Trust store password."
        },
        "certPath": {
          "type": "string",
          "description": "Path to PEM client certificate. Combine with keyPath for mTLS."
        },
        "keyPath": {
          "type": "string",
          "description": "Path to PEM private key (PKCS#8, unencrypted or encrypted). Used with certPath."
        },
        "keyPassword": {
          "type": "string",
          "description": "Password for encrypted PEM private key (PKCS#8 EncryptedPrivateKeyInfo). Omit for unencrypted keys.",
          "writeOnly": true
        },
        "caPath": {
          "type": "string",
          "description": "Path to PEM CA bundle used to build the trust store."
        },
        "protocols": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Allowed TLS protocol versions, e.g. [\"TLSv1.3\"]. Applied via SSLParameters.setProtocols()."
        },
        "ciphers": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "Enabled cipher suites (JSSE names). Applied via SSLParameters.setCipherSuites()."
        }
      },
      "description": "TLS/SSL configuration for HTTPS connections. All fields map to cloud.xcan.angus.spec.model.TlsConfig."
    },
    "blockPrivateNetworks": {
      "type": "boolean",
      "default": false,
      "description": "When true, refuses to connect to loopback/link-local/site-local addresses and cloud metadata endpoints (169.254.169.254). Provides a lightweight SSRF safeguard."
    },
    "manageCookies": {
      "type": "boolean",
      "default": false,
      "description": "When true, persists Set-Cookie responses in a scenario-scoped jar (keyed by scriptId|scenarioName|threadId) and automatically sends matching cookies on subsequent steps. Cookies from the 'cookies' map are still merged in."
    },
    "downloadPath": {
      "type": "string",
      "description": "Optional absolute file path; when set the response body is streamed to this file instead of kept in memory. Use for large downloads. The response body in SampleResult will be null; metadata.downloadedFile contains the file path."
    },
    "maxDurationSeconds": {
      "type": "integer",
      "minimum": 1,
      "description": "Maximum duration in seconds for streaming/SSE mode. Only used with the stream() API."
    },
    "lastEventId": {
      "type": "string",
      "description": "Last-Event-ID header value for SSE reconnection. Sent as the Last-Event-ID header to resume from a specific event."
    }
  },
  "required": [
    "method",
    "url"
  ]
}
