/** * TUS协议头部验证器 * * 该模块实现了TUS协议中各种HTTP头部的验证逻辑 * TUS是一个用于可恢复文件上传的开放协议 * * @version 1.0.0 * @see https://tus.io/protocols/resumable-upload.html */ import { Metadata, TUS_VERSION, TUS_RESUMABLE } from "../utils" /** 验证器函数类型定义,接收可选的字符串值,返回布尔值表示验证结果 */ type validator = (value?: string) => boolean /** * TUS协议头部验证器映射表 * 包含了所有TUS协议规定的HTTP头部的验证规则 */ export const validators = new Map([ [ 'upload-offset', /** * Upload-Offset头部验证 * 用于指示资源内的字节偏移量 * 必须是非负整数 */ (value) => { const n = Number(value) return Number.isInteger(n) && String(n) === value && n >= 0 }, ], [ 'upload-length', /** * Upload-Length头部验证 * 表示整个上传文件的字节大小 * 必须是非负整数 */ (value) => { const n = Number(value) return Number.isInteger(n) && String(n) === value && n >= 0 }, ], [ 'upload-defer-length', /** * Upload-Defer-Length头部验证 * 表示上传大小当前未知,将在后续传输 * 值必须为1,如果长度未延迟则必须省略此头部 */ (value) => value === '1', ], [ 'upload-metadata', /** * Upload-Metadata头部验证 * 必须由一个或多个逗号分隔的键值对组成 * 键和值必须用空格分隔 * 键不能包含空格和逗号且不能为空 * 键应该是ASCII编码,值必须是Base64编码 * 所有键必须唯一 */ (value) => { try { Metadata.parse(value) return true } catch { return false } }, ], [ 'x-forwarded-proto', /** * X-Forwarded-Proto头部验证 * 用于标识客户端与服务器之间的协议 * 仅允许http或https */ (value) => { if (value === 'http' || value === 'https') { return true } return false }, ], [ 'tus-version', /** * Tus-Version头部验证 * 服务器支持的协议版本列表 * 必须按服务器偏好排序,第一个是最优先的 */ (value) => { return TUS_VERSION.includes(value as any) }, ], [ 'tus-resumable', /** * Tus-Resumable头部验证 * 除OPTIONS请求外的每个请求和响应都必须包含 * 值必须是客户端或服务器使用的协议版本 */ (value) => value === TUS_RESUMABLE, ], ['content-type', (value) => value === 'application/offset+octet-stream'], [ 'upload-concat', /** * Upload-Concat头部验证 * 用于部分上传和最终上传的创建请求 * 部分上传值必须为partial * 最终上传值必须以final开头,后跟分号和空格分隔的部分上传URL列表 */ (value) => { if (!value) return false const valid_partial = value === 'partial' const valid_final = value.startsWith('final;') return valid_partial || valid_final }, ], ]) /** * 验证HTTP头部值是否符合TUS协议规范 * @param name 头部名称 * @param value 头部值 * @returns 验证是否通过 */ export function validateHeader(name: string, value?: string): boolean { const lowercaseName = name.toLowerCase() if (!validators.has(lowercaseName)) { return true } return validators.get(lowercaseName)!(value) }