Scryer Prolog中phrase_from_stream/2结合http_open/3非终止问题
问题:Scryer Prolog中phrase_from_stream/2处理HTTP流无法终止
我在使用Scryer Prolog开发包含HTTP客户端组件的应用时,希望通过流式DCG解析数据,但发现http_open/3实例化的流搭配phrase_from_stream/2时会无限循环,无法终止。尝试结合内容大小、设置Connection: close请求头均无效,但相同逻辑在SWI-Prolog中可正常运行。
复现步骤
- 执行
python -m http.server 8000启动本地简易HTTP服务器 - 在Scryer Prolog顶层运行以下代码:
正常工作的场景(先读取整个流再解析)
?- use_module(library(charsio)). true. ?- use_module(library(dcgs)). true. ?- use_module(library(http/http_open)). true. ?- http_open("http://127.0.0.1:8000", Stream, []), get_n_chars(Stream, N, Chars), phrase(seq(Data), Chars). Stream = '$stream'(0x600000636908), N = 726, Chars = "<!DOCTYPE HTML>\n<ht ...", Data = "<!DOCTYPE HTML>\n<ht ...".
无法终止的场景(流式解析)
?- use_module(library(pio)). true. ?- http_open("http://127.0.0.1:8000", Stream, []), phrase_from_stream(seq(Data), Stream). % 无终止,无限循环
SWI-Prolog中的正常表现
?- use_module(library(http/http_open)). true. ?- use_module(library(dcg/basics)). true. ?- use_module(library(pio)). true. ?- http_open("http://127.0.0.1:8000", Stream, []), phrase_from_stream(string(Data), Stream). Stream = <stream>(0x600002954400,0x600002974b00), Data = [60, 33, 68, 79, 67, 84, 89, 80, 69|...] ; false.
调试建议
- 跟踪流的EOF状态:用
stream_property(Stream, eof_at_end(EOF))检查HTTP响应结束后,流是否被正确标记为已到末尾。若未标记EOF,phrase_from_stream/2会持续尝试读取数据。 - 简化DCG规则测试:替换
seq/1为极简规则,比如:
调用read_eof --> [] .phrase_from_stream(read_eof, Stream),看是否能终止,以此排除DCG规则本身的问题。 - 手动限制读取长度:从HTTP响应头中获取
Content-Length,在DCG中只读取对应长度的字符,比如:
结合read_n(0) --> [] . read_n(N) --> [C], { N1 is N-1 }, read_n(N1) .http_open/3的header(Content-Length, Len)参数获取长度,转换为整数后调用phrase_from_stream(read_n(Len), Stream),验证是否能终止。 - 对比本地流测试:用本地文件流替代HTTP流,比如
open("index.html", read, Stream),再调用phrase_from_stream(seq(Data), Stream),若本地流正常,说明问题出在HTTP流的处理逻辑上。 - 检查Scryer Prolog的pio库实现:查看
library(pio)中phrase_from_stream/2的源码,对比SWI-Prolog的实现,重点看EOF检测和流读取的循环逻辑,定位是否存在库函数的bug。
内容的提问来源于stack exchange,提问作者Rodrigo Setti
相关产品推荐
相关产品推荐

