local json = require "json"

--[[print("tshark version: " .. get_version())
print("lua version: " .. _VERSION)]]--

TYPE_STRING=0x00
TYPE_NUMBER=0x01

FRAME_TYPE = {
    DATA = 0x00,
    HEADERS = 0x01,
    PRIORITY = 0x02,
    RST_STREAM = 0x03,
    SETTINGS = 0x04,
    PUSH_PROMISE = 0x05,
    PING = 0x06,
    GOAWAY = 0x07,
    WINDOW_UPDATE = 0x08,
    CONTINUATION = 0x09,
    from_id = function (id)
        local types = {
            "DATA",
            "HEADERS",
            "PRIORITY",
            "RST_STREAM",
            "SETTINGS",
            "PUSH_PROMISE",
            "PING",
            "GOAWAY",
            "WINDOW_UPDATE",
            "CONTINUATION"
        }

        return types[tonumber(id)+1]
    end
}


function optional_isset(str)
    if str == nil then
        return 0
    end
    return 1
end

function optional_string(str)
    if str == nil then
        return ""
    else
        return tostring(str)
    end
end

function optional_number(str)
    if str == nil then
        return 0
    else
        return tonumber(tostring(str))
    end
end

function optional_double(str)
    if str == nil then
        return 0
    else
        return math.floor(tonumber(tostring(str)) * 10000)
    end
end


function optional_array(func, ftype)
    local table = {}
    if func == nil then
        return table
    end
    local ret = {func()} -- cast userdata to array
    if #ret == 0 then
        return table
    end
    for i, field in ipairs(ret) do
        if ftype == TYPE_NUMBER then
            table[i] = optional_number(field.value)
        else
            table[i] = optional_string(field.value)
        end
    end
    return table
end

function make_set_array(str, delim)
    local set = {}
    local array = {}
    for match in string.gmatch(str, "[^"..delim.."]+") do
        set[match] = true
        array[#array+1] = match
    end
    return {set=set, array=array}
end

function encode_http_data(str)
    if str == nil then
        return ""
    end

    local data = tostring(str)
    local encoded_data = ""
    for i=1,math.min(#data, 256) do
        encoded_data = encoded_data .. string.format([[\x%02x]], string.byte(data, i))
    end
    return encoded_data
end

function decode_http2_data(str)
    if str == nil then
        return ""
    end

    str = string.lower(tostring(str))
    local data = ""
    if string.find(data, ":") ~= nil then
        data = string.gsub(string.sub(str, 1, 767), ":", "\\x")
    else
        for i = 1, math.min(#str, 512), 2 do
            data = data .. "\\x" .. string.sub(str, i, i+1)
        end 
    end
    if #data > 0 then
        data = "\\x" .. data
    end

    return data
end

function encode_http2_data(str)
    if str == nil then
        return ""
    end

    local data = tostring(str)
    local encoded_data = ""
    for i=1,#data do
        encoded_data = encoded_data .. string.format([[\x%02x]], string.byte(data, i))
    end
    return encoded_data
end

function http2_type_to_string(id)
    return FRAME_TYPE.from_id(id)
end

function http2_get_value_by_idx(tbl, idx, ftype)
    local ret = nil

    if idx >= 1 or idx <= #tbl then
        ret = tbl[idx]
    end

    if ret == nil and ftype == TYPE_NUMBER then
        ret = 0
    elseif ret == nil  then
        ret = ""
    end

    return ret
end

do
    local dumper_idx = {}
    local tcp_stream_table = {}
    local tcp_stream_table_idx = 1
    local ws_fields = {
        tcp_stream_field = Field.new("tcp.stream"), -- for buckets in table
        frame_protocols_field = Field.new("frame.protocols"),
        frame_time_epoch_field = Field.new("frame.time_epoch"),
        -- ip
        ip_src_field = Field.new("ip.src"),
        ip_dst_field = Field.new("ip.dst"),
        ipv6_src_field = Field.new("ipv6.src"),
        ipv6_dst_field = Field.new("ipv6.dst"),
        -- tcp
        tcp_srcport_field = Field.new("tcp.srcport"),
        tcp_dstport_field = Field.new("tcp.dstport"),
        tcp_ack_field = Field.new("tcp.ack"),
        tcp_seq_field = Field.new("tcp.seq"),
        tcp_nxtseq_field = Field.new("tcp.nxtseq"),
        tcp_checksum_field = Field.new("tcp.checksum"),
        tcp_window_size_value_field = Field.new("tcp.window_size_value"),
        tcp_tsecr_field = Field.new("tcp.options.timestamp.tsecr"),
        tcp_tsval_field = Field.new("tcp.options.timestamp.tsval"),
        tcp_mss_val_field = Field.new("tcp.options.mss_val"),
        tcp_flags_ack_field = Field.new("tcp.flags.ack"),
        tcp_flags_syn_field = Field.new("tcp.flags.syn"),
        tcp_flags_rst_field = Field.new("tcp.flags.reset"),
        tcp_flags_fin_field = Field.new("tcp.flags.fin"),
        tcp_flags_push_field = Field.new("tcp.flags.push"),
        tcp_flags_urg_field = Field.new("tcp.flags.urg"),
        tcp_flags_cwr_field = Field.new("tcp.flags.cwr"),
        tcp_flags_ecn_field = Field.new("tcp.flags.ecn"),
        tcp_options_sack_le_field = Field.new("tcp.options.sack_le"),
        tcp_options_sack_re_field = Field.new("tcp.options.sack_re"),
        tcp_analysis_duplicate_ack_num_field = Field.new("tcp.analysis.duplicate_ack_num"),
        tcp_analysis_ack_rtt_field  = Field.new("tcp.analysis.ack_rtt"),

        tcp_analysis_flags_field = Field.new("tcp.analysis.flags"),
        tcp_analysis_retransmission_field = Field.new("tcp.analysis.retransmission"),
        tcp_analysis_fast_retransmission_field = Field.new("tcp.analysis.fast_retransmission"),
        --- http
        http_request_method_field = Field.new("http.request.method"),
        http_request_full_uri_field = Field.new("http.request.full_uri"),
        http_request_uri_field = Field.new("http.request.uri"),
        http_request_version_field = Field.new("http.request.version"),
        http_response_code_field = Field.new("http.response.code"),
        http_file_data_field = Field.new("http.file_data"),
        http_user_agent_field = Field.new("http.user_agent"),
        http_proxy_connect_host_field = Field.new("http.proxy_connect_host"),
        http_proxy_connect_port_field = Field.new("http.proxy_connect_port"),

        http_header_x_forwarded_for_field = Field.new("http.x_forwarded_for"),
        http_header_x_req_id_field = Field.new("http.header.X-Req-Id"),
        http_header_content_length_field = Field.new("http.content_length"),
        --- http2
        http2_rst_stream_error_field = Field.new("http2.rst_stream.error"),
        http2_streamid_field = Field.new("http2.streamid"),
        http2_stream_dependency_field = Field.new("http2.stream_dependency"),
        http2_settings_id_field = Field.new("http2.settings.id") ,
        http2_settings_header_table_size_field = Field.new("http2.settings.header_table_size"),
        http2_settings_enable_push_field = Field.new("http2.settings.enable_push"),
        http2_settings_max_concurrent_streams_field = Field.new("http2.settings.max_concurrent_streams"),
        http2_settings_initial_window_size_field = Field.new("http2.settings.initial_window_size"),
        http2_settings_unknown_field = Field.new("http2.settings.unknown"),
        http2_magic_field = Field.new("http2.magic"), --- https://tools.ietf.org/html/rfc7540#section-3.5
        http2_window_update_window_size_increment_field = Field.new("http2.window_update.window_size_increment"),
        http2_data_data_field = Field.new("http2.data.data"),
        http2_goaway_last_stream_id_field = Field.new("http2.goaway.last_stream_id"),
        http2_goaway_error_field = Field.new("http2.goaway.error"),
        http2_goaway_addata_field = Field.new("http2.goaway.addata"),
        http2_ping_field = Field.new("http2.ping"),
        http2_pong_field = Field.new("http2.pong"),
        http2_continuation_header_field = Field.new("http2.continuation.header"),
        http2_push_promise_promised_stream_id_field = Field.new("http2.push_promise.promised_stream_id"),
        http2_push_promise_header_field = Field.new("http2.push_promise.header"),
        http2_flags_field = Field.new("http2.flags"),
        http2_flags_end_stream_field = Field.new("http2.flags.end_stream"),
        http2_flags_end_headers_field = Field.new("http2.flags.eh"),
        http2_flags_padded_field = Field.new("http2.flags.padded"),
        http2_flags_priority_field = Field.new("http2.flags.priority"),
        http2_flags_ping_ack_field = Field.new("http2.flags.ack.ping"),
        http2_flags_settings_ack_field = Field.new("http2.flags.ack.settings"),
        http2_type_field = Field.new("http2.type"),
        http2_headers_method_field = Field.new("http2.headers.method"),
        http2_headers_path_field = Field.new("http2.headers.path"),
        http2_headers_status_field = Field.new("http2.headers.status"),
        http2_headers_authority_field = Field.new("http2.headers.authority"),
        http2_headers_user_agent_field = Field.new("http2.headers.user_agent"),
        http2_headers_weight_field = Field.new("http2.headers.weight"),
        --- ssl
        ssl_record_length_field = Field.new("ssl.record.length"),
        ssl_record_content_type_field = Field.new("ssl.record.content_type"),
        ssl_handshake_version_field = Field.new("ssl.handshake.version"),
        ssl_handshake_ciphersuite_field = Field.new("ssl.handshake.ciphersuite"),
        ssl_handshake_random_field = Field.new("ssl.handshake.random"),
        ssl_handshake_extensions_alpn_str_field = Field.new("ssl.handshake.extensions_alpn_str"),
        ssl_handshake_type_field = Field.new("ssl.handshake.type"),
        ssl_alert_message_level_field = Field.new("ssl.alert_message.level"),
        ssl_alert_message_desc_field = Field.new("ssl.alert_message.desc"),
        ssl_ignored_unknown_record_field = Field.new("ssl.ignored_unknown_record")
    }

    local function init_listener()
        local listener = Listener.new("frame")

        function listener.reset()
            -- empty
        end

        function listener.packet(pinfo, tvb, tapinfo)
            local raw_idx = optional_number(ws_fields["tcp_stream_field"]())
            local tcp_stream_idx = raw_idx + 1 -- Lua arrays starts with 1
            local frame_protocols = make_set_array(optional_string(ws_fields["frame_protocols_field"]()), ":")

            -- Fill TCP/IP fields
            local ip_src = ws_fields["ip_src_field"]()
            local ip_dst = ws_fields["ip_dst_field"]()

            if ip_src == nil and ip_dst == nil then
                ip_src = ws_fields["ipv6_src_field"]()
                ip_dst = ws_fields["ipv6_dst_field"]()
            end

            local frame_time_epoch = optional_double(ws_fields["frame_time_epoch_field"]())
            local tcp_srcport = optional_number(ws_fields["tcp_srcport_field"]())
            local tcp_dstport = optional_number(ws_fields["tcp_dstport_field"]())
            local tcp_ack = optional_number(ws_fields["tcp_ack_field"]())
            local tcp_seq = optional_number(ws_fields["tcp_seq_field"]())
            local tcp_nxtseq = optional_number(ws_fields["tcp_nxtseq_field"]())
            local tcp_checksum = optional_number(ws_fields["tcp_checksum_field"]())
            local tcp_window_size_value = optional_number(ws_fields["tcp_window_size_value_field"]())
            local tcp_tsecr = optional_number(ws_fields["tcp_tsecr_field"]())
            local tcp_tsval = optional_number(ws_fields["tcp_tsval_field"]())
            local tcp_mss_val = optional_number(ws_fields["tcp_mss_val_field"]())
            local tcp_flags_ack = optional_number(ws_fields["tcp_flags_ack_field"]())
            local tcp_flags_syn = optional_number(ws_fields["tcp_flags_syn_field"]())
            local tcp_flags_rst = optional_number(ws_fields["tcp_flags_rst_field"]())
            local tcp_flags_fin = optional_number(ws_fields["tcp_flags_fin_field"]())
            local tcp_flags_push = optional_number(ws_fields["tcp_flags_push_field"]())
            local tcp_flags_urg = optional_number(ws_fields["tcp_flags_urg_field"]())
            local tcp_flags_cwr = optional_number(ws_fields["tcp_flags_cwr_field"]())
            local tcp_flags_ecn = optional_number(ws_fields["tcp_flags_ecn_field"]())
            local tcp_options_sack_le = optional_number(ws_fields["tcp_options_sack_le_field"]())
            local tcp_options_sack_re = optional_number(ws_fields["tcp_options_sack_re_field"]())
            local tcp_analysis_duplicate_ack_num = optional_number(ws_fields["tcp_analysis_duplicate_ack_num_field"]())
            local tcp_analysis_ack_rtt = optional_double(ws_fields["tcp_analysis_ack_rtt_field"]())

            local tcp_analysis_flags = optional_isset(ws_fields["tcp_analysis_flags_field"]())
            local tcp_analysis_retransmission = optional_isset(ws_fields["tcp_analysis_retransmission_field"]())
            local tcp_analysis_fast_retransmission = optional_isset(ws_fields["tcp_analysis_fast_retransmission_field"]())
            if tcp_stream_table[tcp_stream_idx] == nil then
                tcp_stream_table[tcp_stream_idx] = {}
                tcp_stream_table_idx = 1
            else
                tcp_stream_table_idx = #tcp_stream_table[tcp_stream_idx] + 1
            end

            tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx] = {
                    frame_protocols = frame_protocols["array"],
                    frame_time_epoch = frame_time_epoch,
                    ip_src = tostring(ip_src),
                    ip_dst = tostring(ip_dst),
                    tcp_srcport = tcp_srcport,
                    tcp_dstport = tcp_dstport,
                    tcp_ack = tcp_ack,
                    tcp_seq = tcp_seq,
                    tcp_nxtseq = tcp_nxtseq,
                    tcp_checksum = tcp_checksum,
                    tcp_window_size_value = tcp_window_size_value,
                    tcp_tsecr = tcp_tsecr,
                    tcp_tsval = tcp_tsval,
                    tcp_mss_val = tcp_mss_val,
                    tcp_flags_ack = tcp_flags_ack,
                    tcp_flags_syn = tcp_flags_syn,
                    tcp_flags_rst = tcp_flags_rst,
                    tcp_flags_fin = tcp_flags_fin,
                    tcp_flags_push = tcp_flags_push,
                    tcp_flags_urg = tcp_flags_urg,
                    tcp_flags_cwr = tcp_flags_cwr,
                    tcp_flags_ecn = tcp_flags_ecn,
                    tcp_options_sack_le = tcp_options_sack_le,
                    tcp_options_sack_re = tcp_options_sack_re,
                    tcp_analysis_duplicate_ack_num = tcp_analysis_duplicate_ack_num,
                    tcp_analysis_ack_rtt = tcp_analysis_ack_rtt,
                    tcp_analysis_flags = tcp_analysis_flags,
                    tcp_analysis_retransmission = tcp_analysis_retransmission,
                    tcp_analysis_fast_retransmission = tcp_analysis_fast_retransmission,
                }

            -- Append SSL fields if it applicable
            if frame_protocols["set"]["ssl"] ~= nil then
                local ssl_record_length = optional_number(ws_fields["ssl_record_length_field"]())
                local ssl_record_content_type = optional_number(ws_fields["ssl_record_content_type_field"]())
                local ssl_handshake_version = optional_number(ws_fields["ssl_handshake_version_field"]())
                local ssl_handshake_ciphersuite = optional_number(ws_fields["ssl_handshake_ciphersuite_field"]())
                local ssl_handshake_random = string.upper(string.gsub(optional_string(ws_fields["ssl_handshake_random_field"]()), ":", ""))
                local ssl_handshake_extensions_alpn_str = optional_string(ws_fields["ssl_handshake_extensions_alpn_str_field"]())
                local ssl_handshake_type = optional_number(ws_fields["ssl_handshake_type_field"]())
                local ssl_alert_message_level = optional_number(ws_fields["ssl_alert_message_level_field"]())
                local ssl_alert_message_desc = optional_number(ws_fields["ssl_alert_message_desc_field"]())
                local ssl_ignored_unknown_record = optional_isset(ws_fields["ssl_ignored_unknown_record_field"]())

                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_record_length"] = ssl_record_length
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_record_content_type"] = ssl_record_content_type
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_handshake_version"] = ssl_handshake_version
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_handshake_ciphersuite"] = ssl_handshake_ciphersuite
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_handshake_random"] = ssl_handshake_random
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_handshake_extensions_alpn_str"] = ssl_handshake_extensions_alpn_str
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_handshake_type"] = ssl_handshake_type
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_alert_message_level"] = ssl_alert_message_level
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_alert_message_desc"] = ssl_alert_message_desc
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["ssl_ignored_unknown_record"] = ssl_ignored_unknown_record
            end

            -- Append HTTP fields if it applicable
            if frame_protocols["set"]["http"] ~= nil then
                local http_request_method = optional_string(ws_fields["http_request_method_field"]())
                local http_request_full_uri = optional_string(ws_fields["http_request_full_uri_field"]())
                local http_request_uri = optional_string(ws_fields["http_request_uri_field"]())
                local http_request_version = optional_string(ws_fields["http_request_version_field"]())
                local http_response_code = optional_number(ws_fields["http_response_code_field"]())
                local http_file_data = encode_http_data(optional_string(ws_fields["http_file_data_field"]()))
                local http_user_agent = optional_string(ws_fields["http_user_agent_field"]())
                local http_proxy_connect_host = optional_string(ws_fields["http_proxy_connect_host_field"]())
                local http_proxy_connect_port = optional_number(ws_fields["http_proxy_connect_port_field"]())
                local http_header_x_forwarded_for = optional_string(ws_fields["http_header_x_forwarded_for_field"]())
                local http_header_x_req_id = optional_string(ws_fields["http_header_x_req_id_field"]())
                local http_header_content_length = optional_number(ws_fields["http_header_content_length_field"]())

                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"] = {}
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_request_method"] = http_request_method
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_request_full_uri"] = http_request_full_uri
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_request_uri"] = http_request_uri
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_request_version"] = http_request_version
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_response_code"] = http_response_code
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_file_data"] = http_file_data
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_user_agent"] = http_user_agent
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_proxy_connect_host"] = http_proxy_connect_host
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_proxy_connect_port"] = http_proxy_connect_port

                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_header_x_forwarded_for"] = http_header_x_forwarded_for
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_header_x_req_id"] = http_header_x_req_id
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http"]["http_header_content_length"] = http_header_content_length
            end

            if frame_protocols["set"]["http2"] ~= nil then
                local http2_type = optional_array(ws_fields["http2_type_field"], TYPE_NUMBER)

                local http2_rst_stream_error = optional_array(ws_fields["http2_rst_stream_error_field"], TYPE_NUMBER)
                local http2_streamid = optional_array(ws_fields["http2_streamid_field"], TYPE_NUMBER)
                local http2_stream_dependency = optional_array(ws_fields["http2_stream_dependency_field"], TYPE_NUMBER)
                local http2_settings_id = optional_array(ws_fields["http2_settings_id_field"], TYPE_NUMBER)
                local http2_settings_header_table_size = optional_array(ws_fields["http2_settings_header_table_size_field"], TYPE_NUMBER)
                local http2_settings_enable_push = optional_array(ws_fields["http2_settings_enable_push_field"], TYPE_NUMBER)
                local http2_settings_max_concurrent_streams = optional_array(ws_fields["http2_settings_max_concurrent_streams_field"], TYPE_NUMBER)
                local http2_settings_initial_window_size = optional_array(ws_fields["http2_settings_initial_window_size_field"], TYPE_NUMBER)
                local http2_settings_unknown = optional_array(ws_fields["http2_settings_unknown_field"], TYPE_NUMBER)
                local http2_magic = encode_http2_data(ws_fields["http2_magic_field"]())
                local http2_window_update_window_size_increment = optional_array(ws_fields["http2_window_update_window_size_increment_field"], TYPE_NUMBER)
                local http2_data_data = optional_array(ws_fields["http2_data_data_field"], TYPE_STRING)
                local http2_goaway_last_stream_id = optional_array(ws_fields["http2_goaway_last_stream_id_field"], TYPE_NUMBER)
                local http2_goaway_error = optional_array(ws_fields["http2_goaway_error_field"], TYPE_NUMBER)
                local http2_goaway_addata = optional_array(ws_fields["http2_goaway_addata_field"], TYPE_STRING)
                local http2_ping = optional_array(ws_fields["http2_ping_field"], TYPE_STRING)
                local http2_pong = optional_array(ws_fields["http2_pong_field"], TYPE_STRING)
                local http2_continuation_header = optional_array(ws_fields["http2_continuation_header_field"], TYPE_STRING)
                local http2_push_promise_promised_stream_id = optional_array(ws_fields["http2_push_promise_promised_stream_id_field"], TYPE_NUMBER)
                local http2_push_promise_header = optional_array(ws_fields["http2_push_promise_header_field"], TYPE_STRING)
                local http2_flags = optional_array(ws_fields["http2_flags_field"], TYPE_NUMBER)
                local http2_flags_end_stream = optional_array(ws_fields["http2_flags_end_stream_field"], TYPE_NUMBER)
                local http2_flags_end_headers = optional_array(ws_fields["http2_flags_end_headers_field"], TYPE_NUMBER)
                local http2_flags_padded = optional_array(ws_fields["http2_flags_padded_field"], TYPE_NUMBER)
                local http2_flags_priority = optional_array(ws_fields["http2_flags_priority_field"], TYPE_NUMBER)
                local http2_flags_ping_ack = optional_array(ws_fields["http2_flags_ping_ack_field"], TYPE_NUMBER)
                local http2_flags_settings_ack = optional_array(ws_fields["http2_flags_settings_ack_field"], TYPE_NUMBER)
                local http2_headers_method = optional_array(ws_fields["http2_headers_method_field"], TYPE_STRING)
                local http2_headers_path = optional_array(ws_fields["http2_headers_path_field"], TYPE_STRING)
                local http2_headers_status = optional_array(ws_fields["http2_headers_status_field"], TYPE_NUMBER)
                local http2_headers_authority = optional_array(ws_fields["http2_headers_authority_field"], TYPE_STRING)
                local http2_headers_user_agent = optional_array(ws_fields["http2_headers_user_agent_field"], TYPE_STRING)
                local http2_headers_weight = optional_array(ws_fields["http2_headers_weight_field"], TYPE_NUMBER)

                -- Split HTTP2 fields
                local h2_frame_number = 1
                local h2_data_data_count = 1
                local h2_rst_stream = 1
                local h2_headers = 1
                local h2_window_update = 1
                local h2_settings = 1
                local h2_go_away = 1
                local h2_ping = 1
                local h2_cont = 1
                local h2_push_promise = 1
                local h2_weight = 1
                -- deep copy of ip, tcp, ssl levels is very expensive
                -- put all http2 data to one array
                tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"] = {}
                for i, h2_type in ipairs(http2_type) do
                    http2_table_idx = #tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"] + 1
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx] = {}

                    -- Fill common fields
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_atype"] = http2_type_to_string(h2_type)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_streamid"] = http2_streamid[i]
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_stream_dependency"] = http2_get_value_by_idx(http2_stream_dependency, h2_frame_number, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags"] = http2_get_value_by_idx(http2_flags, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_end_stream"] = http2_get_value_by_idx(http2_flags_end_stream, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_end_headers"] = http2_get_value_by_idx(http2_flags_end_headers, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_padded"] = http2_get_value_by_idx(http2_flags_padded, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_priority"] = http2_get_value_by_idx(http2_flags_priority, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_ping_ack"] = http2_get_value_by_idx(http2_flags_ping_ack, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_flags_settings_ack"] = http2_get_value_by_idx(http2_flags_settings_ack, i, TYPE_NUMBER)
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_type"] = h2_type
                    tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_magic"] = http2_magic

                    h2_frame_number = h2_frame_number + 1

                    -- add data only to DATA frame
                    if h2_type == FRAME_TYPE.DATA then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_data_data"] = decode_http2_data(http2_get_value_by_idx(http2_data_data, h2_data_data_count, TYPE_STRING))
                        h2_data_data_count = h2_data_data_count + 1
                    end

                    -- add headers info frame
                    if h2_type == FRAME_TYPE.HEADERS then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_method"] = http2_get_value_by_idx(http2_headers_method, h2_headers, TYPE_STRING)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_path"] = http2_get_value_by_idx(http2_headers_path, h2_headers, TYPE_STRING)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_status"] = http2_get_value_by_idx(http2_headers_status, h2_headers, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_authority"] = http2_get_value_by_idx(http2_headers_authority, h2_headers, TYPE_STRING)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_user_agent"] = http2_get_value_by_idx(http2_headers_user_agent, h2_headers, TYPE_STRING)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_headers_weight"] = http2_get_value_by_idx(http2_headers_weight, h2_weight, TYPE_STRING)
                        h2_weight = h2_weight + 1
                        h2_headers = h2_headers + 1
                    end

                    -- add priority info frame
                    if h2_type == FRAME_TYPE.PRIORITY then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_priority_weight"] = http2_get_value_by_idx(http2_headers_weight, h2_weight, TYPE_STRING)
                        h2_weight = h2_weight + 1
                    end

                    -- add rst stream info frame
                    local local_http2_rst_stream_error = 0
                    if h2_type == FRAME_TYPE.RST_STREAM then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_rst_stream_error"] = http2_get_value_by_idx(http2_rst_stream_error, h2_rst_stream, TYPE_NUMBER)
                        h2_rst_stream = h2_rst_stream + 1
                    end

                    -- add settings info frame
                    if h2_type == FRAME_TYPE.SETTINGS then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_settings_header_table_size"] = http2_get_value_by_idx(http2_settings_header_table_size, h2_settings, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_settings_enable_push"] = http2_get_value_by_idx(http2_settings_enable_push, h2_settings, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_settings_max_concurrent_streams"] = http2_get_value_by_idx(http2_settings_max_concurrent_streams, h2_settings, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_settings_initial_window_size"] = http2_get_value_by_idx(http2_settings_initial_window_size, h2_settings, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_settings_unknown"] = http2_get_value_by_idx(http2_settings_unknown, h2_settings, TYPE_NUMBER)
                        h2_settings = h2_settings + 1
                    end

                    -- add push promise info frame
                    if h2_type == FRAME_TYPE.PUSH_PROMISE then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_push_promise_promised_stream_id"] = http2_get_value_by_idx(http2_push_promise_promised_stream_id, h2_push_promise, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_push_promise_header"] = http2_get_value_by_idx(http2_push_promise_header, h2_push_promise, TYPE_STRING)
                        h2_push_promise = h2_push_promise + 1
                    end

                    -- add ping info frame
                    if h2_type == FRAME_TYPE.PING then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_ping"] = http2_get_value_by_idx(http2_ping, h2_ping, TYPE_STRING)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_pong"] = http2_get_value_by_idx(http2_pong, h2_ping, TYPE_STRING)
                        h2_ping = h2_ping + 1
                    end

                    --- go away
                    if h2_type == FRAME_TYPE.GOAWAY then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_goaway_last_stream_id"] = http2_get_value_by_idx(http2_goaway_last_stream_id, h2_go_away, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_goaway_error"] = http2_get_value_by_idx(http2_goaway_error, h2_go_away, TYPE_NUMBER)
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_goaway_addata"] = http2_get_value_by_idx(http2_goaway_addata, h2_go_away, TYPE_STRING)
                        h2_go_away = h2_go_away + 1
                    end

                    -- window update header
                    if h2_type == FRAME_TYPE.WINDOW_UPDATE then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_window_update_window_size_increment"] = http2_get_value_by_idx(http2_window_update_window_size_increment, h2_window_update, TYPE_NUMBER)
                        h2_window_update = h2_window_update + 1
                    end
                    -- continuation
                    if h2_type == FRAME_TYPE.CONTINUATION then
                        tcp_stream_table[tcp_stream_idx][tcp_stream_table_idx]["http2"][http2_table_idx]["http2_continuation_header"] = http2_get_value_by_idx(http2_continuation_header, h2_cont, TYPE_STRING)
                        h2_cont = h2_cont + 1
                    end
                end
            end
        end

        function listener.draw()
            for i=1,#tcp_stream_table do
                print(json.encode(tcp_stream_table[i]))
            end
        end
    end

    init_listener()
end
