handle method

Performs req and returns the proxied response, or an error response on a rejected target / transport failure.

Implementation

Future<HttpProxyResponse> handle(HttpProxyRequest req) async {
  final uri = Uri.tryParse(req.url);
  if (uri == null || !uri.hasScheme) {
    return _error(req, 'invalid url');
  }
  if (uri.scheme != 'https') {
    return _error(req, 'only https is allowed');
  }
  if (!_allowedHosts.contains(uri.host)) {
    return _error(req, 'host not allowed: ${uri.host}');
  }

  final headers = <String, String>{...req.headers};
  final Uri target;
  try {
    target = _applyCredentials(req, uri, headers);
  } on _CredentialError catch (e) {
    return _error(req, e.message);
  }

  final stopwatch = Stopwatch()..start();
  try {
    final request = http.Request(req.method, target)
      ..headers.addAll(headers)
      ..body = req.body;
    final streamed = await httpClient.send(request).timeout(timeout);
    final response = await http.Response.fromStream(streamed);
    stopwatch.stop();
    // Report the real upstream duration so a proxied client measures the
    // model's generation speed without the browser↔Hub round-trip (see
    // [aiRequestMs]).
    return HttpProxyResponse(
      requestId: req.requestId,
      statusCode: response.statusCode,
      headers: {
        ...response.headers,
        kAiProxyElapsedMsHeader: '${stopwatch.elapsedMilliseconds}',
      },
      body: response.body,
    );
  } on Object catch (e) {
    return _error(req, 'request failed: $e');
  }
}