// Reading the next request head under the header deadline, and the status
// each failure is answered with.

///|
/// How reading the next request head ended.
priv enum HeadOutcome {
  /// A request head, parsed and validated.
  Request(@h1.ParsedRequest)
  /// The peer ended its stream, or the header deadline ran out, before a
  /// byte of a request arrived: the connection closes without an answer.
  Idle
  /// The head is refused: answer with `status` and close.
  Refuse(status~ : Int32, error~ : @http.TransportError)
  /// Reading failed: close without an answer.
  Lost(@http.TransportError)
}

///|
/// Reads the next request head within `header_timeout_ms`. Empty lines
/// before a request line are skipped (RFC 9112, section 2.2) and count
/// against `max_header_bytes`. Bytes after the head stay pending.
async fn Wire::head(self : Wire, limits : ServerLimits) -> HeadOutcome noraise {
  let mut started = !self.pending.is_empty()
  let mut outcome = Idle
  let completed = self.timer.within(limits.header_timeout_ms, () => {
    let decoder = @h1.HeadDecoder::new(
      max=limits.max_header_bytes,
      skip_leading_crlf=true,
    )
    outcome = for ;; {
      let bytes = self.read(io_bytes, Head) catch { error => break Lost(error) }
      guard bytes is Some(bytes) else {
        break if started {
          refuse_head(
            @h1.ParseError(kind=Malformed, detail="truncated request head"),
            limits,
          )
        } else {
          Idle
        }
      }
      started = true
      let result = decoder.feed(bytes) catch {
        error => break refuse_head(error, limits)
      }
      if result.head is Some(head) {
        self.unread(bytes[result.consumed:])
        break try
          @h1.parse_request_head(head, limits={
            ..@h1.Limits::default(),
            max_head_bytes: limits.max_header_bytes,
          })
        catch {
          error => refuse_head(error, limits)
        } noraise {
          request => Request(request)
        }
      }
    }
  })
  if completed {
    outcome
  } else if started {
    Refuse(
      status=408,
      error=timeout(limits.header_timeout_ms, Head, "the request head"),
    )
  } else {
    Idle
  }
}

///|
/// The answer to a head the codec refused: 431 for a head past its limits,
/// 505 for an HTTP version other than 1.x, 400 otherwise (smuggling
/// included).
fn refuse_head(error : @h1.ParseError, limits : ServerLimits) -> HeadOutcome {
  let @h1.ParseError(kind~, detail~) = error
  match kind {
    HeadTooLarge =>
      Refuse(
        status=431,
        error=@http.TransportError(
          kind=TooLarge(limit=limits.max_header_bytes),
          phase=Head,
          detail~,
        ),
      )
    Unsupported =>
      Refuse(
        status=505,
        error=@http.TransportError(kind=Protocol, phase=Head, detail~),
      )
    _ =>
      Refuse(
        status=400,
        error=@http.TransportError(kind=Protocol, phase=Head, detail~),
      )
  }
}

///|
/// The status for a body framing the codec refused: 501 for a transfer
/// coding other than a single `chunked`, 400 for smuggling (CL with TE) and
/// for a duplicate or invalid Content-Length.
fn framing_status(kind : @h1.ParseKind) -> Int32 {
  match kind {
    Unsupported => 501
    _ => 400
  }
}

///|
/// The status that answers a request body failure: 413 past the body limit,
/// 408 past its deadline, 400 for anything else (bad chunk framing, a chunk
/// line or trailers past their limits, early EOF).
fn body_status(error : @http.TransportError) -> Int32 {
  match error {
    @http.TransportError(kind=TooLarge(..), ..) => 413
    @http.TransportError(kind=Timeout(..), ..) => 408
    _ => 400
  }
}