Tak więc piszę aplikację do wąchania pakietów. Zasadniczo chciałem go powąchać dla sesji tcp, a następnie przeanalizować je, aby sprawdzić, czy są one http, a jeśli są, i czy mają odpowiedni typ zawartości, itp., Zapisz je jako plik na moim dysku twardym.Attoparsec przydziela masę pamięci na dużą rozmowę "weź"
W tym celu chciałem, aby był wydajny. Ponieważ obecna biblioteka http jest oparta na łańcuchach i będę zajmować się dużymi plikami, a ja naprawdę potrzebowałem tylko parsować odpowiedzi HTTP, postanowiłem przetasować własne w attoparsec.
Kiedy skończyłem program, znalazłem, że kiedy analizowałem 9 megabajtową odpowiedź http z plikiem wav, kiedy profilowałem to przydzielanie gig pamięci podczas próby sparsowania ciała odpowiedzi http. Kiedy patrzę na HTTP.prof widzę pewne linie:
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
tak widać, gdzieś w httpbody, wziąć nazywa się 1201 razy, powodując ponad 500 (+++) powiązań, o bytestrings, co powoduje absurdalna ilość przydziału pamięci.
Oto kod. N to tylko długość treści odpowiedzi http, jeśli taka istnieje. Jeśli go nie ma, po prostu próbuje zabrać wszystko.
Chciałem, aby zwrócił on leniwy test składający się z 1000 lub więcej znaków bytowych, ale nawet jeśli zmienię go tak, by po prostu wziął n i zwrócił surowy test, wciąż ma w sobie te alokacje (i wykorzystuje 14 gig pamięci).
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)
Czytałem bloga przez faceta, który był kombinatorem i miał ten sam problem, ale nigdy nie słyszałem o rezolucji. Czy ktokolwiek kiedykolwiek napotkał ten problem wcześniej lub znalazł rozwiązanie?
Edycja: OK, cóż, zostawiłem to cały dzień i nic nie dostałem. Po zbadaniu problemu nie sądzę, że istnieje sposób, aby to zrobić bez dodawania leniwego bytestringa do attoparsec. Spojrzałem też na wszystkie inne biblioteki i albo brakowało im świadectw testowych, albo innych rzeczy.
Więc znalazłem obejście. Jeśli myślisz o żądaniu http, to nagłówek, nowa linia, nowa linia, treść. Ponieważ ciało jest ostatnie, a parsowanie zwraca krotkę zarówno z tego, co przeanalizowałeś, jak i tego, co pozostało z analizy, mogę pominąć analizowanie ciała wewnątrz attoparsec i zamiast tego wyskubać ciało z pominiętego testu bytowego.
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 }
Jest trochę brudny, ale ostatecznie działa szybko i nie przydziela niczego więcej, niż chciałem. Zasadniczo więc spasujesz na podstawie zbierania danych, zbierając struktury danych http, a następnie pomiędzy kolekcjami, sprawdzam długość zawartości struktury, którą właśnie dostałem, odejmuję odpowiednią ilość z pozostałego testu bytestring, a następnie kontynuuję, jeśli istnieje jakaś próba z lewej strony.
Edytuj: Skończyłem ten projekt. Działa jak marzenie. Nie jestem odpowiednio kabalizowany, ale jeśli ktoś chce zobaczyć całe źródło, możesz go znaleźć pod adresem https://github.com/onmach/Audio-Sniffer.
Co to jest AC? (wypełniacz) –
Czy używasz [defragmentacji operacji blaze-buildera] (http://lambda-view.blogspot.com/2010/11/defragmenting-lazy-bytestrings.html) to naprawia? –
AC jest kwalifikowanym importem dla Data.Attoparsec.Char8 –