如何返回错误

为了简化错误处理,pingora-error crate 导出了一个自定义的 Result 类型,广泛用于其他 Pingora crates。

Result 的错误变体中的 Error 结构体是对任意错误类型的封装。它允许用户标记底层错误的来源,并附加其他自定义的上下文信息。

用户通常需要通过传播已有错误或创建全新的错误来返回错误。pingora-error 提供了便捷的错误构建函数来实现这一点。

示例

例如,当某个预期的请求头不存在时,可以返回一个错误:

  1. fn validate_req_header(req: &RequestHeader) -> Result<()> {
  2. // 验证 `host` 请求头是否存在
  3. req.headers()
  4. .get(http::header::HOST)
  5. .ok_or_else(|| Error::explain(InvalidHTTPHeader, "未检测到 host 请求头"))
  6. }
  7. impl MyServer {
  8. pub async fn handle_request_filter(
  9. &self,
  10. http_session: &mut Session,
  11. ctx: &mut CTX,
  12. ) -> Result<bool> {
  13. validate_req_header(session.req_header()?).or_err(HTTPStatus(400), "缺少必要的请求头")?;
  14. Ok(true)
  15. }
  16. }

如果未找到 host 请求头,validate_req_header 会返回一个使用 Error::explain 创建的新 Error,同时附带相关类型(如 InvalidHTTPHeader)和可记录在错误日志中的上下文信息。

这个错误最终会传播到请求过滤器,并通过 or_err 返回一个新的 HTTPStatus 错误。作为默认的 pingora-proxyfail_to_proxy() 阶段的一部分,此错误不仅会被记录,还会向下游发送一个 400 Bad Request 的响应。

请注意,原始引发的错误也会在错误日志中可见。or_err 会将原始错误封装在一个新的错误中,并添加上下文信息,而 ErrorDisplay 实现会打印导致错误的整个链条。

指南

一个错误通常包含以下部分:

  • 类型(如 ConnectionClosed
  • 来源(如 UpstreamDownstreamInternal
  • 原因(可选,被封装的另一个错误)
  • 上下文(可选,用户提供的任意字符串描述)

可以使用如 new_in / new_up / new_down 之类的函数创建最小错误,每个函数都指定一个来源并要求用户提供错误类型。

通常:

  • 如果需要创建一个没有直接原因但带有更多上下文的新错误,使用 Error::explain。也可以使用 explain_err 替换 Result 中的潜在错误为新错误。
  • 如果需要将引发的错误封装到一个带有更多上下文的新错误中,使用 Error::because。也可以使用 or_errResult 中的潜在错误替换为包含原始错误的新错误。

重试

错误可以是“可重试的”。如果错误是可重试的,pingora-proxy 将允许对上游请求进行重试。一些错误仅在重用连接时可重试,例如处理远端断开尝试重用的连接的情况。

默认情况下,新创建的 Error 会继承直接引发错误的重试状态;如果未指定,则默认视为不可重试。