Domanda

Così sto scrivendo un packet sniffing app. Fondamentalmente ho voluto che annusare per le sessioni TCP, e quindi analizzare loro per vedere se sono http, e se lo sono, e se hanno il giusto tipo di contenuto, ecc, salvarli come un file sul mio disco rigido.

Quindi, a tal fine, ho voluto che fosse efficiente. Dal momento che la corrente http biblioteca è corda a base di, e io sarò fare con file di grandi dimensioni, e ho solo davvero bisogno di analizzare le risposte HTTP, ho deciso di rotolare il mio in attoparsec.

Quando ho finito il mio programma, ho scoperto che quando mi è stato l'analisi di un meg http risposta 9 con un file wav in esso, quando ho fatto il profilo, è stata l'assegnazione di un giga di memoria quando stava cercando di analizzare fuori il corpo di la risposta HTTP. Quando guardo il HTTP.prof vedo alcune linee:

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

Quindi, come si può vedere, da qualche parte all'interno httpbody, take viene chiamato 1201 volte, causando 500 + (+++) concatenazioni di stringhe di byte, che causa una quantità assurda di allocazione di memoria.

Ecco il codice. N è solo la lunghezza del contenuto della risposta HTTP, se ce n'è uno. Se non c'è uno solo cerca di prendere tutto.

ho voluto che per restituire un bytestring pigro di 1000 o così stringhe di byte di caratteri, ma anche se cambio di prendere solo n e restituire un bytestring rigorosa, ha ancora quei allocazioni in esso (e utilizza 14 giga di memoria) .


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)

I stava leggendo un blog da un ragazzo che ha fatto combinatorrent e lui stava avendo lo stesso problema, ma non ho mai sentito parlare di una risoluzione. Qualcuno ha mai imbattuto in questo problema prima o trovato una soluzione?

Edit: Ok, ho lasciato questo in su l'intera giornata e non ha ottenuto nulla. Dopo aver studiato il problema non credo che ci sia un modo per farlo senza l'aggiunta di una funzione di accesso bytestring pigro per attoparsec. Ho anche guardato tutte le altre biblioteche e sia mancava stringhe di byte o altre cose.

Così ho trovato una soluzione. Se pensate a una richiesta HTTP, va intestazioni, ritorno a capo, ritorno a capo, il corpo. Dal momento che il corpo è l'ultima, e l'analisi restituisce una tupla sia con quello che analizzato e che cosa è restanti del bytestring, posso saltare l'analisi del corpo all'interno attoparsec e invece colgo il corpo dritto al largo della bytestring che è a sinistra.


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 }

E 'un po' disordinato, ma alla fine funziona veloce e alloca niente di più di quanto avrei voluto. Quindi, in pratica si piega sopra le http strutture dati bytestring di raccolta, poi tra le collezioni, verifico la lunghezza del contenuto della struttura Ho appena ricevuto, tirare una quantità appropriata dal restante bytestring, e poi continuare se c'è qualche bytestring sinistra.

Edit: Io in realtà finito questo progetto. Funziona come un fascino. Io non cabalized correttamente, ma se qualcuno vuole vedere l'intera sorgente, si può trovare a https: // github.com/onmach/Audio-Sniffer .

È stato utile?

Soluzione

combinatorrent ragazzo qui:)

Se la memoria non serve, il problema con attoparsec è che l'input richieste un po 'alla volta, costruendo un bytestring pigro che è finalmente concatenato. La mia "soluzione" è stato quello di lanciare la funzione di ingresso me stesso. Cioè, ho il flusso di input per attoparsec da una presa di rete e so quanti byte aspettarsi in un messaggio. Fondamentalmente, ho diviso in due casi:

  • Il messaggio è piccolo:. Leggi fino a 4K dalla presa e mangiare che Bytestring un po 'alla volta (fette di stringhe di byte sono veloci e noi buttare via il 4k dopo che è stato esaurito)

  • Il messaggio è "grande" (grande qui significa circa il 16 Kilobyte nel parlare BitTorrent): Calcoliamo quanto il 4k pezzo che abbiamo in grado di compiere, e quindi abbiamo semplicemente chiediamo la presa di rete sottostante per riempire le cose in. ora abbiamo due stringhe di byte, la parte restante del 4k pezzo e il grande pezzo. Essi hanno tutti i dati, in modo da concatenare quelli e li analisi in è quello che facciamo.

    Si può essere in grado di ottimizzare la fase di concatenazione via.

Il TL; DR versione:. Ho gestirlo attoparsec fuori e handroll il ciclo per evitare il problema

Il combinatorrent rilevante commettere è fc131fe24, vedere

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

per i dettagli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top