Skip to content

http

http

NIP-66 HTTP metadata container with header extraction capabilities.

Captures Server and X-Powered-By HTTP headers from the WebSocket upgrade handshake response as part of NIP-66 monitoring. Supports both clearnet and overlay network relays (overlay networks require a SOCKS5 proxy).

Note

Headers are captured using aiohttp's TraceConfig hooks during the WebSocket upgrade handshake, not from a separate HTTP request. This ensures the captured headers reflect the actual relay WebSocket endpoint rather than a potentially different HTTP endpoint.

For clearnet relays, SSL verification is enabled by default. When allow_insecure=True, a non-validating SSL context (CERT_NONE) is used instead. Overlay networks always use CERT_NONE because the proxy provides encryption. This is the only NIP-66 test that supports both clearnet and overlay networks.

See Also

bigbrotr.nips.nip66.data.Nip66HttpData: Data model for HTTP header fields. bigbrotr.nips.nip66.logs.Nip66HttpLogs: Log model for HTTP extraction results. bigbrotr.nips.nip11.info.Nip11InfoMetadata: NIP-11 info retrieval that also makes HTTP requests to relays (but uses Accept: application/nostr+json for JSON document retrieval).

Classes

Nip66HttpMetadata

Bases: BaseNipMetadata

Container for HTTP header data and extraction logs.

Provides the execute() class method that initiates a WebSocket connection and captures server identification headers from the upgrade response.

See Also

bigbrotr.nips.nip66.nip66.Nip66: Top-level model that orchestrates this alongside other tests. bigbrotr.models.metadata.MetadataType: The NIP66_HTTP variant used when storing these results.

Functions
execute async classmethod
execute(
    relay: Relay,
    timeout: float | None = None,
    proxy_url: str | None = None,
    *,
    allow_insecure: bool = False,
) -> Self

Extract HTTP headers from a relay's WebSocket handshake response.

Parameters:

  • relay (Relay) –

    Relay to connect to.

  • timeout (float | None, default: None ) –

    Connection timeout in seconds (default: 10.0).

  • proxy_url (str | None, default: None ) –

    Optional SOCKS5 proxy URL (required for overlay networks).

  • allow_insecure (bool, default: False ) –

    Use non-validating SSL for clearnet relays.

Returns:

  • Self

    An Nip66HttpMetadata instance with header data and logs.

Source code in src/bigbrotr/nips/nip66/http.py
@classmethod
async def execute(
    cls,
    relay: Relay,
    timeout: float | None = None,  # noqa: ASYNC109
    proxy_url: str | None = None,
    *,
    allow_insecure: bool = False,
) -> Self:
    """Extract HTTP headers from a relay's WebSocket handshake response.

    Args:
        relay: Relay to connect to.
        timeout: Connection timeout in seconds (default: 10.0).
        proxy_url: Optional SOCKS5 proxy URL (required for overlay networks).
        allow_insecure: Use non-validating SSL for clearnet relays.

    Returns:
        An ``Nip66HttpMetadata`` instance with header data and logs.
    """
    timeout = timeout if timeout is not None else DEFAULT_TIMEOUT
    logger.debug("http_testing relay=%s timeout_s=%s proxy=%s", relay.url, timeout, proxy_url)

    overlay_networks = (NetworkType.TOR, NetworkType.I2P, NetworkType.LOKI)
    if proxy_url is None and relay.network in overlay_networks:
        return cls(
            data=Nip66HttpData(),
            logs=Nip66HttpLogs(
                success=False,
                reason=f"overlay network {relay.network.value} requires proxy",
            ),
        )

    logs: dict[str, Any] = {"success": False, "reason": None}
    data: dict[str, Any] = {}

    try:
        data = await cls._http(relay, timeout, proxy_url, allow_insecure=allow_insecure)
        if data:
            logs["success"] = True
            logger.debug(
                "http_completed relay=%s server=%s", relay.url, data.get("http_server")
            )
        else:
            logs["reason"] = "no HTTP headers captured"
            logger.debug("http_no_data relay=%s", relay.url)
    except (OSError, TimeoutError, aiohttp.ClientError) as e:
        logs["reason"] = str(e) or type(e).__name__
        logger.debug("http_error relay=%s error=%s", relay.url, str(e))

    return cls(
        data=Nip66HttpData.model_validate(Nip66HttpData.parse(data)),
        logs=Nip66HttpLogs.model_validate(logs),
    )