Question

Je suis en train d'écrire une application reniflage de paquets. Au fond, je voulais à renifler pour les sessions de tcp, puis les analyser pour voir si elles sont http, et si elles sont, et si elles ont le type de contenu à droite, etc, les sauver dans un fichier sur mon disque dur.

Donc, à cette fin, je voulais que ce soit efficace. Étant donné que la bibliothèque http actuelle est une chaîne basée, et je vais être traiter de gros fichiers, et je ne vraiment besoin pour analyser http réponses, j'ai décidé de rouler dans mon attoparsec.

Quand je fini mon programme, je l'ai trouvé que lorsque je l'analyse d'un 9 meg http réponse avec un fichier wav en elle, quand je PROFILES, il allouait un concert de mémoire quand il a essayé d'analyser le corps la réponse http. Quand je regarde le HTTP.prof je vois quelques lignes:

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

Comme vous pouvez le voir, quelque part dans httpbody, prendre est appelé 1201 fois, ce qui 500+ (+++) concaténations de chaînes d'octets, ce qui provoque une quantité absurde d'allocation de mémoire.

Voici le code. N est juste la longueur contenu de la réponse http, s'il y a un. S'il n'y en a pas, il essaie juste de prendre tout.

Je voulais à retourner un bytestring paresseux de 1000 ou si caractère des chaînes ordinaires, mais même si je changer pour ne prendre que n et retourner une bytestring stricte, il a encore les allocations en (et il utilise 14 Go de mémoire) .


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)

J'étais en train de lire un blog par le gars qui a fait combinatorrent et il avait la même question, mais je jamais entendu parler d'une résolution. Quelqu'un at-il jamais courir à travers ce problème avant ou trouvé une solution?

Edit: Bon, eh bien je quitte cette place toute la journée et rien obtenu. Après avoir étudié le problème, je ne pense pas qu'il y ait un moyen de le faire sans l'ajout d'un accesseur bytestring paresseux pour attoparsec. J'ai aussi regardé toutes les autres bibliothèques et soit ils manquais ou autres chaînes d'octets.

Je trouve une solution de contournement. Si vous pensez à une requête http, il va les en-têtes, saut de ligne, retour à la ligne, le corps. Puisque le corps est le dernier et l'analyse renvoie un tuple à la fois ce que vous analysables et ce qui reste de la bytestring, je peux sauter l'analyse du corps à l'intérieur attoparsec et à la place cueille le corps droit au large de la bytestring qui reste.


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 }

Il est un peu désordonné, mais finalement ça marche rapide et Alloue rien de plus que je voulais. Donc, fondamentalement, vous Replier les bytestring collecte des structures de données http, puis entre les deux collections, je vérifie la longueur du contenu de la structure que je viens de recevoir, tirer une quantité appropriée du reste bytestring, puis continuer en cas de ByteString gauche.

Edit: En fait, je fini vers le haut de ce projet. Fonctionne comme un charme. Je n'est pas cabalized correctement, mais si quelqu'un veut voir l'ensemble de la source, vous pouvez le trouver à https: // github.com/onmach/Audio-Sniffer .

Était-ce utile?

La solution

combinatorrent gars ici:)

Si ma mémoire est bonne, le problème avec attoparsec est que l'entrée de demande un petit peu à la fois, la construction d'un bytestring paresseux qui est finalement concaténé. Ma « solution » devait rouler la fonction d'entrée moi-même. C'est, je reçois le flux d'entrée pour attoparsec à partir d'une prise réseau et je sais combien d'octets à attendre dans un message. Au fond, je divisé en deux cas:

  • Le message est petit. Lire jusqu'à 4k de la prise et de manger qui ByteString un peu à la fois (tranches de chaînes d'octets sont rapides et nous jeter 4k après qu'il a été épuisé)

  • Le message est « grand » (grand signifie ici environ 16 kilo-octet en parler chaque): On calcule combien le 4k morceau que nous avons peut remplir, et nous demandons simplement la prise réseau sous-jacent pour remplir les choses. nous avons maintenant deux, la partie des chaînes ordinaires restante du morceau 4k et le gros morceau. Ils ont toutes les données, donc concaténer ceux-ci et les analyse en est ce que nous faisons.

    Vous pouvez être en mesure d'optimiser l'étape de concaténation loin.

TL; version DR:. Je le manipuler en dehors de attoparsec et handroll la boucle pour éviter le problème

Le combinatorrent concerné commit est fc131fe24, voir

https://github.com/jlouis/combinatorrent/commit/fc131fe24207909dd980c674aae6aaba27b966d4

pour les détails.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top