Pregunta

Así que estoy escribiendo una aplicación para la detección de paquetes. Básicamente quería que olfateo de sesiones TCP, y luego analizarlos para ver si son http, y si lo son, y si tienen el tipo de contenido, etc., guardarlos como un archivo en el disco duro.

Así que, con ese fin, yo quería que fuera eficaz. Puesto que la corriente http biblioteca se basa cadena, y voy a ser tratar con archivos de gran tamaño, y lo único que realmente necesita para analizar las respuestas HTTP, decidí rodar en mi propia attoparsec.

Cuando terminé mi programa, encontró que cuando estaba analizar un meg http respuesta 9 con un archivo WAV en ella, cuando perfiladas que, se asigna un giga de memoria cuando se trata de analizar el cuerpo de la respuesta HTTP. Cuando miro el HTTP.prof veo algunas líneas:

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

Así como se puede ver, en algún lugar dentro httpbody, toma se llama 1201 veces, causando más de 500 (+++) concatenaciones de cadenas de bytes, lo que provoca una absurda cantidad de asignación de memoria.

Este es el código. N es sólo la longitud del contenido de la respuesta HTTP, si es que existe. Si no hay uno solo que trata de tomar todo.

Yo quería que devolver una cadena de bytes perezosa de 1000 o por lo cadenas de bytes de caracteres, pero incluso si lo cambio a acaba de tomar n y devuelve una cadena de bytes estricta, todavía tiene esas asignaciones en ella (y que utiliza 14 GB de 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)

Yo estaba leyendo un blog por el tipo que hizo combinatorrent y que estaba teniendo el mismo problema, pero nunca he oído de una resolución. ¿Alguna vez alguien correr a través de este problema antes o encontrado una solución?

Edit: Bueno, así me dejó esto todo el día y nada consiguió. Después de investigar el problema no creo que hay una manera de hacerlo sin la adición de un descriptor de acceso cadena de bytes perezoso para attoparsec. También miré a todas las otras bibliotecas y estas carecían de cadenas de bytes u otras cosas.

Así que encontró una solución. Si se piensa en una petición HTTP, que va cabeceras, nuevas líneas de nueva línea, cuerpo. Puesto que el cuerpo es pasado, y el análisis devuelve una tupla tanto con lo que se analiza y lo que queda de la cadena de bytes, que puede saltar al analizar el cuerpo en el interior y en su lugar attoparsec arranco el cuerpo directamente de la cadena de bytes que se deja.


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 }

Es un poco desordenado, pero en última instancia, funciona rápido y asigna nada más de lo que quería. Así que, básicamente, se dobla sobre las http estructuras de datos cadena de bytes que recogen, a continuación, en el medio colecciones, puedo comprobar la longitud del contenido de la estructura acabo de recibir, tirar de una cantidad apropiada de la restante cadena de bytes, y luego continuar por si hay alguna cadena de bytes a la izquierda.

Editar: En realidad terminó este proyecto. Funciona de maravilla. I no es cabalized correctamente, pero si alguien quiere ver todo el código fuente, se puede encontrar en https: // github.com/onmach/Audio-Sniffer .

¿Fue útil?

Solución

combinatorrent tipo aquí:)

Si no recuerdo mal, el problema es que con attoparsec entrada de demandas un poco a la vez, la creación de una cadena de bytes perezosa que finalmente se concatena. Mi "solución" fue para rodar la función de entrada a mí mismo. Es decir, me sale el flujo de entrada de attoparsec de una toma de red y sé la cantidad de bytes que se espera en un mensaje. Básicamente, lo dividieron en dos casos:

  • El mensaje es pequeña:. Leer hasta 4k de la toma y come lo que ByteString un poco a la vez (rebanadas de cadenas de bytes son rápidos y tirar la 4k después de que haya sido agotado)

  • El mensaje es "grande" (grande aquí significa alrededor de 16 kilobytes de hablar bittorrent): Calculamos la cantidad de la 4k trozo tenemos puede cumplir, y luego simplemente solicitamos la toma de red subyacente para llenar las cosas en. ahora tenemos dos cadenas de bytes, la parte restante del trozo 4k y el pedazo grande. Tienen todos los datos, por lo que la concatenación de los análisis y en que es lo que hacemos.

    Es posible que pueda optimizar el paso de la concatenación de distancia.

El TL; versión DR:. I manejarlo attoparsec exterior y handroll el bucle para evitar el problema

El combinatorrent relevante es cometer fc131fe24, consulte

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

para los detalles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top