API
REST API 接口
沙箱服务提供 REST API 接口来在受限制的环境中运行程序(默认监听于 localhost:5050
)。
- /run POST 在受限制的环境中运行程序(下面有例子)
- /file GET 得到所有在文件存储中的文件 ID 到原始命名映射
- /file POST 上传一个文件到文件存储,返回一个文件 ID 用于提供给 /run 接口
- /file/:fileId GET 下载文件 ID 指定的文件
- /file/:fileId DELETE 删除文件 ID 指定的文件
- /ws /run 接口的 WebSocket 版
- /stream 运行交互式命令
- /version 得到本程序编译版本和 go 语言运行时版本
- /config 得到本程序部分运行参数,包括沙箱详细参数
REST API 接口定义
typescript
interface LocalFile {
src: string; // 文件绝对路径
}
interface MemoryFile {
content: string | Buffer; // 文件内容
}
interface PreparedFile {
fileId: string; // 文件 id
}
interface Collector {
name: string; // copyOut 文件名
max: number; // 最大大小限制
pipe?: boolean; // 通过管道收集(默认值为false文件收集)
}
interface Symlink {
symlink: string; // 符号连接目标 (v1.6.0+)
}
interface StreamIn {
streamIn: boolean; // 流式输入 (v1.8.1+)
}
interface StreamOut {
streamOut: boolean; // 流式输出 (v1.8.1+)
}
interface Cmd {
args: string[]; // 程序命令行参数
env?: string[]; // 程序环境变量
// 指定 标准输入、标准输出和标准错误的文件 (null 是为了 pipe 的使用情况准备的,而且必须被 pipeMapping 的 in / out 指定)
files?: (LocalFile | MemoryFile | PreparedFile | Collector | StreamIn | StreamOut | null)[];
tty?: boolean; // 开启 TTY (需要保证标准输出和标准错误为同一文件)同时需要指定 TERM 环境变量 (例如 TERM=xterm)
// 资源限制
cpuLimit?: number; // CPU时间限制,单位纳秒
clockLimit?: number; // 等待时间限制,单位纳秒 (通常为 cpuLimit 两倍)
memoryLimit?: number; // 内存限制,单位 byte
stackLimit?: number; // 栈内存限制,单位 byte
procLimit?: number; // 线程数量限制
cpuRateLimit?: number; // 仅 Linux,CPU 使用率限制,1000 等于单核 100%
cpuSetLimit?: string; // 仅 Linux,限制 CPU 使用,使用方式和 cpuset cgroup 相同 (例如,`0` 表示限制仅使用第一个核)
strictMemoryLimit?: boolean; // deprecated: 使用 dataSegmentLimit (这个选项依然有效)
dataSegmentLimit?: boolean; // 仅linux,开启 rlimit 堆空间限制(如果不使用cgroup默认开启)
addressSpaceLimit?: boolean; // 仅linux,开启 rlimit 虚拟内存空间限制(非常严格,在所以申请时触发限制)
// 在执行程序之前复制进容器的文件列表
copyIn?: {[dst:string]:LocalFile | MemoryFile | PreparedFile | Symlink};
// 在执行程序后从容器文件系统中复制出来的文件列表
// 在文件名之后加入 '?' 来使文件变为可选,可选文件不存在的情况不会触发 FileError
copyOut?: string[];
// 和 copyOut 相同,不过文件不返回内容,而是返回一个对应文件 ID ,内容可以通过 /file/:fileId 接口下载
copyOutCached?: string[];
// 指定 copyOut 复制文件大小限制,单位 byte
copyOutMax?: number;
}
enum Status {
Accepted = 'Accepted', // 正常情况
MemoryLimitExceeded = 'Memory Limit Exceeded', // 内存超限
TimeLimitExceeded = 'Time Limit Exceeded', // 时间超限
OutputLimitExceeded = 'Output Limit Exceeded', // 输出超限
FileError = 'File Error', // 文件错误
NonzeroExitStatus = 'Nonzero Exit Status', // 非 0 退出值
Signalled = 'Signalled', // 进程被信号终止
InternalError = 'Internal Error', // 内部错误
}
interface PipeIndex {
index: number; // cmd 的下标
fd: number; // cmd 的 fd
}
interface PipeMap {
in: PipeIndex; // 管道的输入端
out: PipeIndex; // 管道的输出端
// 开启管道代理,传输内容会从输出端复制到输入端
// 输入端内容在输出端关闭以后会丢弃 (防止 SIGPIPE )
proxy?: boolean;
name?: string; // 如果代理开启,内容会作为 copyOut 放在输入端 (用来 debug )
// 限制 copyOut 的最大大小,代理会在超出大小之后正常复制
max?: number;
}
enum FileErrorType {
CopyInOpenFile = 'CopyInOpenFile',
CopyInCreateFile = 'CopyInCreateFile',
CopyInCopyContent = 'CopyInCopyContent',
CopyOutOpen = 'CopyOutOpen',
CopyOutNotRegularFile = 'CopyOutNotRegularFile',
CopyOutSizeExceeded = 'CopyOutSizeExceeded',
CopyOutCreateFile = 'CopyOutCreateFile',
CopyOutCopyContent = 'CopyOutCopyContent',
CollectSizeExceeded = 'CollectSizeExceeded',
}
interface FileError {
name: string; // 错误文件名称
type: FileErrorType; // 错误代码
message?: string; // 错误信息
}
interface Request {
requestId?: string; // 给 WebSocket 使用来区分返回值的来源请求
cmd: Cmd[];
pipeMapping: PipeMap[];
}
interface CancelRequest {
cancelRequestId: string; // 取消某个正在进行中的请求
};
// WebSocket 请求
type WSRequest = Request | CancelRequest;
interface Result {
status: Status;
error?: string; // 详细错误信息
exitStatus: number; // 程序返回值
time: number; // 程序运行 CPU 时间,单位纳秒
memory: number; // 程序运行内存,单位 byte
procPeak?: number; // 程序运行最大线程数量(需要内核版本>=6.1,且开启 cgroup v2)
runTime: number; // 程序运行现实时间,单位纳秒
// copyOut 和 pipeCollector 指定的文件内容
files?: {[name:string]:string};
// copyFileCached 指定的文件 id
fileIds?: {[name:string]:string};
// 文件错误详细信息
fileError?: FileError[];
}
// WebSocket 结果
interface WSResult {
requestId: string;
results: Result[];
error?: string;
}
// 流式请求 / 响应
interface Resize {
index: number;
fd: number;
rows: number;
cols: number;
x: number;
y: number;
}
interface Input {
index: number;
fd: number;
content: Buffer;
}
interface Output {
index: number;
fd: number;
content: Buffer;
}
WebSocket 流接口
WebSocket 流接口是用于运行一个程序,同时和它的输入输出进行交互。所有的消息都应该使用 WebSocket 的 binary 格式来发送来避免兼容性问题。
text
+--------+--------+---...
| 类型 | 载荷 ...
+--------|--------+---...
请求:
请求类型 =
1 - 运行请求 (载荷 = JSON 编码的请求体)
2 - 设置终端窗口大小 (载荷 = JSON 编码的请求体)
3 - 输入 (载荷 = 1 字节 (4 位的 命令下标 + 4 位的 文件描述符) + 输入内容)
4 - 取消 (没有载荷)
响应:
响应类型 =
1 - 运行结果 (载荷 = JSON 编码的运行结果)
2 - 输出 (载荷 = 1 字节 (4 位的 命令下标 + 4 位的 文件描述符) + 输入内容)
任何的不完整,或者不合法的消息会被认为是错误,并终止运行。