所以我正在写一个包嗅探应用程序。基本上,我希望它嗅探TCP会话,然后分析它们,看看它们是否是HTTP,如果是,以及它们是否具有正确的内容类型等,将它们保存为硬盘上的文件。
因此,为了达到这个目的,我希望它是有效率的。由于当前的HTTP库是基于字符串的,我将处理大型文件,而且我只需要解析HTTP响应,所以我决定在Attoparsec中使用自己的库。
当我完成程序时,我发现当我用一个wav文件解析一个9meg的HTTP响应时,当我分析它时,它正在分配一个gig内存,当它试图解析HTTP响应的主体时。当我查看http.prof时,我看到一些行:
httpBody Main 362 1 0.0 0.0 93.8 99.3
take Data.Attoparsec.Internal 366 1201 0.0 0.0 93.8 99.3
takeWith Data.Attoparsec.Internal 367 3603 0.0 0.0 93.8 99.3
demandInput Data.Attoparsec.Internal 375 293 0.0 0.0 93.8 99.2
prompt Data.Attoparsec.Internal 378 293 0.0 0.0 93.8 99.2
+++ Data.Attoparsec.Internal 380 586 93.8 99.2 93.8 99.2
正如您所看到的,take在httpbody中的某个地方被称为1201次,导致500+(+++)个字节串串联,这导致了大量的内存分配。
这是密码。n只是HTTP响应的内容长度(如果有)。如果没有一个,它只会试图拿走一切。
我希望它返回1000个左右字符字节的懒惰字节串,但是即使我把它改为只接受n并返回严格的字节串,它仍然有这些分配(它使用14千兆内存)。
httpBody n = do
x <- if n > 0
then AC.take n
else AC.takeWhile (\_ -> True)
if B.length x == 0
then return Nothing
else return (Just x)
我在读一篇由Combinator的人写的博客,他也有同样的问题,但我从来没有听说过解决方案。以前有没有人遇到过这个问题或找到了解决方案?
编辑:好吧,我一整天都把这个忘了,什么也没得到。在研究了这个问题之后,我认为没有一种方法可以做到这一点,而不需要在ATTOParsec中添加一个懒惰的bytesting访问器。我还查看了所有其他的图书馆,它们要么缺少索引,要么缺少其他东西。
所以我找到了一个解决办法。如果您考虑一个HTTP请求,它会出现headers、newline、newline和body。因为主体是最后一个,解析返回一个包含您解析的内容和字节字符串剩余内容的元组,所以我可以跳过在Attoparsec中解析主体,而直接从剩余的字节字符串中提取主体。
parseHTTPs bs = if P.length results == 0
then Nothing
else Just results
where results = foldParse(bs, [])
foldParse (bs,rs) = case ACL.parse httpResponse bs of
ACL.Done rest r -> addBody (rest,rs) r
otherwise -> rs
addBody (rest,rs) http = foldParse (rest', rs')
where
contentlength = ((read . BU.toString) (maybe "0" id (hdrContentLength (rspHeaders http))))
rest' = BL.drop contentlength rest
rs' = rs ++ [http { rspBody = body' }]
body'
| contentlength == 0 = Just rest
| BL.length rest == 0 = Nothing
| otherwise = Just (BL.take contentlength rest)
httpResponse = do
(code, desc) <- statusLine
hdrs <- many header
endOfLine
-- body <- httpBody ((read . BU.toString) (maybe "0" id (hdrContentLength parsedHeaders)))
return Response { rspCode = code, rspReason = desc, rspHeaders = parseHeaders hdrs, rspBody = undefined }
它有点凌乱,但最终它工作得很快,分配的内容不超过我想要的。因此,基本上,您可以折叠收集HTTP数据结构的字节集,然后在两个集合之间,检查刚获得的结构的内容长度,从剩余的字节集中提取适当的量,然后继续执行是否还有任何字节集。
编辑:我实际上完成了这个项目。很有魅力。我没有被正确的屏蔽,但是如果有人想查看整个源代码,你可以在
https://github.com/onmach/Audio-Sniffer
.