class Mu::Pcap::Reader::HttpFamily

Reader for HTTP family of protocols (HTTP/SIP/RTSP). Handles message boundaries and decompressing/dechunking payloads.

Constants

CRLF
FAMILY
RE_CHUNKED
RE_CHUNK_SIZE_LINE

Returns array containing raw and dechunked payload. Returns nil if payload cannot be completely read.

RE_CONTENT_ENCODING
RE_CONTENT_LENGTH
RE_CONTENT_LENGTH_SIP
RE_HEADERS_COMPLETE
RE_REQUEST_LINE

Request line e.g. GET /index.html HTTP/1.1

RE_STATUS_LINE

Status line e.g. SIP/2.0 404 Authorization required

Public Instance Methods

family() click to toggle source
# File lib/woolen_common/pcap/mu/pcap/reader/http_family.rb, line 20
def family
    FAMILY
end
get_chunks(bytes) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/reader/http_family.rb, line 125
def get_chunks bytes
    raw = []
    dechunked = []
    io = StringIO.new bytes
    until io.eof?
        # Read size line
        size_line = io.readline
        raw << size_line
        if size_line =~ RE_CHUNK_SIZE_LINE
            chunk_size = $1.to_i(16)
        else
            # Malformed chunk size line
            $stderr.puts "malformed size line : #{size_line.inspect}"
            return nil
        end

        # Read chunk data
        chunk = io.read(chunk_size)
        if chunk.size < chunk_size
            # malformed/incomplete
            $stderr.puts "malformed/incomplete #{chunk_size}"
            return nil
        end
        raw << chunk
        dechunked << chunk
        # Get end-of-chunk CRLF
        crlf = io.read(2)
        if crlf == CRLF
            raw << crlf
        else
            # CRLF has not arrived or, if this is the last chunk,
            # we might be looking at the first two bytes of a trailer
            # and we don't support trailers (see rfc 2616 sec3.6.1).
            return nil
        end

        if chunk_size == 0
            # Done. Return raw and dechunked payloads.
            return raw.join, dechunked.join
        end
    end

    # EOF w/out reaching last chunk.
    return nil
end

Private Instance Methods

do_read_message!(bytes, state=nil) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/reader/http_family.rb, line 47
def do_read_message! bytes, state=nil
    case bytes
        when RE_REQUEST_LINE
            proto = $3
        when RE_STATUS_LINE
            proto = $2
            status = $3.to_i
            if state
                requests = state[:requests] ||= []
                if requests[0] == "HEAD"
                    reply_to_head = true
                end
                if status > 199
                    # We have a final response. Forget about request.
                    requests.shift
                end
            end
        else
            return nil # Not http family.
    end

    # Read headers
    if bytes =~ RE_HEADERS_COMPLETE
        headers = $&
        rest = $'
    else
        return nil
    end
    message = [headers]

    # Read payload.
    if proto == 'SIP'
        re_content_length = RE_CONTENT_LENGTH_SIP
    else
        re_content_length = RE_CONTENT_LENGTH
    end
    if reply_to_head
        length = 0
    elsif headers =~ RE_CHUNKED
        # Read chunks, dechunking in runtime case.
        raw, dechunked = get_chunks(rest)
        if raw
            length = raw.length
            payload = raw
        else
            return nil # Last chunk not received.
        end
    elsif headers =~ re_content_length
        length = $3.to_i
        if rest.length >= length
            payload = rest.slice(0, length)
        else
            return nil # Not enough bytes.
        end
    else
        # XXX. When there is a payload and no content-length
        # header HTTP RFC says to read until connection close.
        length = 0
    end

    message << payload

    # Consume message from input bytes.
    message_len = headers.length + length
    if bytes.length >= message_len
        bytes.slice!(0, message_len)
        return message.join
    else
        return nil # Not enough bytes.
    end
end
do_record_write(bytes, state=nil) click to toggle source
# File lib/woolen_common/pcap/mu/pcap/reader/http_family.rb, line 24
def do_record_write bytes, state=nil
    return if not state
    if bytes =~ RE_REQUEST_LINE
        method = $1
        requests = state[:requests] ||= []
        requests << method
    end
end