function parseMultipartFormDataHeaders (input, position) {
  // 1. Let name, filename and contentType be null.
  let name = null
  let filename = null
  let contentType = null
  let encoding = null

  // 2. While true:
  while (true) {
    // 2.1. If position points to a sequence of bytes starting with 0x0D 0x0A (CR LF):
    if (input[position.position] === 0x0d && input[position.position + 1] === 0x0a) {
      // 2.1.1. If name is null, return failure.
      if (name === null) {
        throw parsingError('header name is null')
      }

      // 2.1.2. Return name, filename and contentType.
      return { name, filename, contentType, encoding }
    }

    // 2.2. Let header name be the result of collecting a sequence of bytes that are
    //      not 0x0A (LF), 0x0D (CR) or 0x3A (:), given position.
    let headerName = collectASequenceOfBytes(
      (char) => char !== 0x0a && char !== 0x0d && char !== 0x3a,
      input,
      position
    )

    // 2.3. Remove any HTTP tab or space bytes from the start or end of header name.
    headerName = removeChars(headerName, true, true, (char) => char === 0x9 || char === 0x20)

    // 2.4. If header name does not match the field-name token production, return failure.
    if (!HTTP_TOKEN_CODEPOINTS.test(headerName.toString())) {
      throw parsingError('header name does not match the field-name token production')
    }

    // 2.5. If the byte at position is not 0x3A (:), return failure.
    if (input[position.position] !== 0x3a) {
      throw parsingError('expected :')
    }

    // 2.6. Advance position by 1.
    position.position++

    // 2.7. Collect a sequence of bytes that are HTTP tab or space bytes given position.
    //      (Do nothing with those bytes.)
    collectASequenceOfBytes(
      (char) => char === 0x20 || char === 0x09,
      input,
      position
    )

    // 2.8. Byte-lowercase header name and switch on the result:
    switch (bufferToLowerCasedHeaderName(headerName)) {
      case 'content-disposition': {
        // 1. Set name and filename to null.
        name = filename = null

        // 2. If position does not point to a sequence of bytes starting with
        //    `form-data; name="`, return failure.
        if (!bufferStartsWith(input, formDataNameBuffer, position)) {
          throw parsingError('expected form-data; name=" for content-disposition header')
        }

        // 3. Advance position so it points at the byte after the next 0x22 (")
        //    byte (the one in the sequence of bytes matched above).
        position.position += 17

        // 4. Set name to the result of parsing a multipart/form-data name given
        //    input and position, if the result is not failure. Otherwise, return
        //    failure.
        name = parseMultipartFormDataName(input, position)

        // 5. If position points to a sequence of bytes starting with `; filename="`:
        if (input[position.position] === 0x3b /* ; */ && input[position.position + 1] === 0x20 /* ' ' */) {
          const at = { position: position.position + 2 }

          if (bufferStartsWith(input, filenameBuffer, at)) {
            if (input[at.position + 8] === 0x2a /* '*' */) {
              at.position += 10 // skip past filename*=

              // Remove leading http tab and spaces. See RFC for examples.
              // https://datatracker.ietf.org/doc/html/rfc6266#section-5
              collectASequenceOfBytes(
                (char) => char === 0x20 || char === 0x09,
                input,
                at
              )

              const headerValue = collectASequenceOfBytes(
                (char) => char !== 0x20 && char !== 0x0d && char !== 0x0a, // ' ' or CRLF
                input,
                at
              )

              if (
                (headerValue[0] !== 0x75 && headerValue[0] !== 0x55) || // u or U
                (headerValue[1] !== 0x74 && headerValue[1] !== 0x54) || // t or T
                (headerValue[2] !== 0x66 && headerValue[2] !== 0x46) || // f or F
                headerValue[3] !== 0x2d || // -
                headerValue[4] !== 0x38 // 8
              ) {
                throw parsingError('unknown encoding, expected utf-8\'\'')
              }

              // skip utf-8''
              filename = decodeURIComponent(new TextDecoder().decode(headerValue.subarray(7)))

              position.position = at.position
            } else {
              // 1. Advance position so it points at the byte after the next 0x22 (") byte
              //    (the one in the sequence of bytes matched above).
              position.position += 11

              // Remove leading http tab and spaces. See RFC for examples.
              // https://datatracker.ietf.org/doc/html/rfc6266#section-5
              collectASequenceOfBytes(
                (char) => char === 0x20 || char === 0x09,
                input,
                position
              )

              position.position++ // skip past " after removing whitespace

              // 2. Set filename to the result of parsing a multipart/form-data name given
              //    input and position, if the result is not failure. Otherwise, return failure.
              filename = parseMultipartFormDataName(input, position)
            }
          }
        }

        break
      }
      case 'content-type': {
        // 1. Let header value be the result of collecting a sequence of bytes that are
        //    not 0x0A (LF) or 0x0D (CR), given position.
        let headerValue = collectASequenceOfBytes(
          (char) => char !== 0x0a && char !== 0x0d,
          input,
          position
        )

        // 2. Remove any HTTP tab or space bytes from the end of header value.
        headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)

        // 3. Set contentType to the isomorphic decoding of header value.
        contentType = isomorphicDecode(headerValue)

        break
      }
      case 'content-transfer-encoding': {
        let headerValue = collectASequenceOfBytes(
          (char) => char !== 0x0a && char !== 0x0d,
          input,
          position
        )

        headerValue = removeChars(headerValue, false, true, (char) => char === 0x9 || char === 0x20)

        encoding = isomorphicDecode(headerValue)

        break
      }
      default: {
        // Collect a sequence of bytes that are not 0x0A (LF) or 0x0D (CR), given position.
        // (Do nothing with those bytes.)
        collectASequenceOfBytes(
          (char) => char !== 0x0a && char !== 0x0d,
          input,
          position
        )
      }
    }

    // 2.9. If position does not point to a sequence of bytes starting with 0x0D 0x0A
    //      (CR LF), return failure. Otherwise, advance position by 2 (past the newline).
    if (input[position.position] !== 0x0d && input[position.position + 1] !== 0x0a) {
      throw parsingError('expected CRLF')
    } else {
      position.position += 2
    }
  }
}
@see https://andreubotella.github.io/multipart-form-data/#parse-multipart-form-data-headers
@param {Buffer} input
@param {{ position: number }} position