grok2api / app /platform /net /grpc.py
FUCAT's picture
Deploy grok2api to HF Spaces (Docker)
7e55e53
Raw
History Blame Contribute Delete
5.11 kB
"""gRPC-Web framing and status helpers.
Encodes data frames for gRPC-Web requests and parses response frames,
including trailer extraction and gRPC status code mapping.
"""
import base64
import json
import re
import struct
from dataclasses import dataclass
from typing import Dict, List, Mapping, Optional, Tuple
from urllib.parse import unquote
from app.platform.logging.logger import logger
_B64_RE = re.compile(rb"^[A-Za-z0-9+/=\r\n]+$")
# gRPC status code → HTTP equivalent (best-effort mapping).
_GRPC_HTTP: Dict[int, int] = {
0: 200,
4: 504,
7: 403,
8: 429,
14: 503,
16: 401,
}
@dataclass(frozen=True)
class GrpcStatus:
code: int
message: str = ""
@property
def ok(self) -> bool:
return self.code == 0
@property
def http_equiv(self) -> int:
return _GRPC_HTTP.get(self.code, 502)
class GrpcClient:
"""gRPC-Web framing helpers.
All methods are static; instantiate only if you want a namespace handle.
"""
# ------------------------------------------------------------------
# Encoding
# ------------------------------------------------------------------
@staticmethod
def encode_payload(data: bytes) -> bytes:
"""Wrap *data* in a gRPC-Web data frame (flag=0x00)."""
return b"\x00" + struct.pack(">I", len(data)) + data
# ------------------------------------------------------------------
# Decoding
# ------------------------------------------------------------------
@staticmethod
def _maybe_decode_base64(body: bytes, content_type: Optional[str]) -> bytes:
ct = (content_type or "").lower()
if "grpc-web-text" in ct:
return base64.b64decode(b"".join(body.split()), validate=False)
head = body[: min(len(body), 2048)]
if head and _B64_RE.fullmatch(head):
compact = b"".join(body.split())
try:
return base64.b64decode(compact, validate=True)
except ValueError:
pass
return body
@staticmethod
def _parse_trailers(payload: bytes) -> Dict[str, str]:
text = payload.decode("utf-8", errors="replace")
result: Dict[str, str] = {}
for line in re.split(r"\r\n|\n", text):
if ":" not in line:
continue
k, v = line.split(":", 1)
result[k.strip().lower()] = v.strip()
if "grpc-message" in result:
result["grpc-message"] = unquote(result["grpc-message"])
return result
@classmethod
def parse_response(
cls,
body: bytes,
content_type: Optional[str] = None,
headers: Optional[Mapping[str, str]] = None,
) -> Tuple[List[bytes], Dict[str, str]]:
"""Parse a gRPC-Web response body.
Returns ``(messages, trailers)`` where *messages* is a list of
raw protobuf payloads and *trailers* contains gRPC metadata
(including ``grpc-status`` and ``grpc-message``).
"""
decoded = cls._maybe_decode_base64(body, content_type)
messages: List[bytes] = []
trailers: Dict[str, str] = {}
i, n = 0, len(decoded)
while i < n:
if n - i < 5:
break
flag = decoded[i]
length = int.from_bytes(decoded[i + 1: i + 5], "big")
i += 5
if n - i < length:
break
payload = decoded[i: i + length]
i += length
if flag & 0x80:
trailers.update(cls._parse_trailers(payload))
elif flag & 0x01:
raise ValueError("grpc-web compressed frame is not supported")
else:
messages.append(payload)
# Supplement from HTTP headers (some servers send trailers as headers).
if headers:
lower = {k.lower(): v for k, v in headers.items()}
for key in ("grpc-status", "grpc-message"):
if key in lower and key not in trailers:
val = str(lower[key]).strip()
trailers[key] = unquote(val) if key == "grpc-message" else val
raw_code = str(trailers.get("grpc-status", "")).strip()
try:
code = int(raw_code)
except ValueError:
code = -1
if code not in (0, -1):
logger.error(
"grpc response reported error: grpc_status={} grpc_message={} content_type={}",
code,
trailers.get("grpc-message", ""),
content_type or "",
)
return messages, trailers
@staticmethod
def get_status(trailers: Mapping[str, str]) -> GrpcStatus:
"""Extract ``GrpcStatus`` from parsed trailers."""
raw = str(trailers.get("grpc-status", "")).strip()
msg = str(trailers.get("grpc-message", "")).strip()
try:
code = int(raw)
except ValueError:
code = -1
return GrpcStatus(code=code, message=msg)
__all__ = ["GrpcClient", "GrpcStatus"]