go-judge
go-judge copied to clipboard
在用了除 gRPC 外其他传输层时,无法向 sandbox 中添加二进制文件
在使用除 gRPC 外其他传输层时(HTTP / WebSocket / FFI 等)时,数据都是以 JSON 方式传递的,然而 struct CmdFile 里的 Content 被声明称了 *string
类型:
https://github.com/criyle/go-judge/blob/e5c7b811e5c72f5e7fb494c8e4914a5b0729ed6c/cmd/executorserver/model/model.go#L14-L22
也就是说,似乎是没办法把一个二进制文件作为调用 /run
时的 copyIn 直接扔到沙盒里去.我尝试了下面几种方法:
- 使用 README 中的方式,传一个 Buffer 进去,但是直接返回
json: cannot unmarshal object into Go struct field CmdFile.cmd.files.content of type string
. - 使用
buffer.toString('binary')
,这样倒是可以传进沙盒,但是沙盒里面的文件会变,比如一个值(十进制)为 $200$ 的 byte 会变成 $195$ 和 $136$ 两个 byte.
如果仅是这样那么问题还不是很大,至少可以通过 file post 先把文件传入沙盒,再用 fileId 来在 cmd 中使用文件.但是 FFI 传输层中的 FileAdd
仍然是接受 JSON 字符串作为参数.也就是说,在使用 FFI 时,这个问题目前无解.
顺便提个建议,由于存在二进制文件,且 JSON 里没有对应 byte 的类型,JSON 不太适合作为这个项目的信息传输格式.在假设把 CmdFile 里的 Content 重构成 []byte
类型后,一个 byte 在 JSON 中会占用四个 byte 的空间(包含三个十进制数位和一个逗号),因此建议采用一些更加适合的格式.需要注意的是,BSON 由于其 16 MB 的最大文档大小,同样也不是一个适合的格式.或许 MessagePack 是一个较优的选择.
定义成 *string
类型的考虑是可以直接传递字面值来作为文件内容。通常来说文件也是以 UTF-8 编码的。在 go 中,如果定义的是 []byte
来接受 json 值的话要经过 base64 的编码。
在大多数的情况下文件是由 src
来传递来自本机文件系统的文件,可以支持二进制文件。
FFI 有段时间没有更新过了,考虑到没有已知的使用者,可以借此机会更新接口实现。
从支持二进制编码来考虑的话,也许 gRPC 使用的 protobuf 可以作为备选的一种格式。使用 base64 编码的字符串在 json 中也可行。
protobuf 的话有个小问题,就是传文件的时候会经常套娃.
就比如 cmd_file 里有个 file,file 里有个 memory_file,memory_file 有个 content 这样的.
定义成 *string 类型的考虑是可以直接传递字面值来作为文件内容。通常来说文件也是以 UTF-8 编码的。在 go 中,如果定义的是 []byte 来接受 json 值的话要经过 base64 的编码。
所以说 JSON 就不太适合这种出现了二进制数据的场合,不管是编码成十进制整数字面量数组还是 base64 都会增加数据的长度.本来传输题目的测试数据这种东西就不是为了「人类可读性」的,结合这一点,换用二进制编码的数据交换格式可能更合适.
在设计的时候,沙箱服务通常作为一个附属组件的方式和评测服务部署在同一个 host 或者是共享卷宗的 pod 里面。在 FFI 的使用情况下甚至是同一个进程。在这种部署下,测试数据是由 src 路径来提供,并且支持非 UTF-8 文件。
content 提供内容是为了自测这种使用示例,用来提供非持久化的测试数据。而在这种情况下,用户提供的数据通常来自于网页上的文本框并由 JSON 编码传递的 UTF-8 内容。
在这个设计下,让 Rest API 或者 FFI 来支持 content 传递非 UTF-8 文件似乎是一个伪需求。
现在在使用 WebSocket 作为 API 接口时,可以传递二进制数据给输入输出了。