http2_headers_frame_header.h
http2_headers_frame_trailer.cc
http2_headers_frame_trailer.h
+ http2_headers_frame_with_startline.cc
+ http2_headers_frame_with_startline.h
http2_hpack.cc
http2_hpack.h
http2_hpack_dynamic_table.cc
The HTTP/2 inspector (H2I) converts HTTP/2 frames into HTTP/1.1 message sections and feeds them
to the new HTTP inspector (NHI) for further processing.
-The Http2StreamSplitter strips the frame headers from the frame data and stores them in separate
-buffers. As in NHI, long data frames are split into 16kb chunks for inspection. If a header frame
-is followed by continuation frames, all the header frames are flushed together for inspection. The
-frame headers from each frame are stored contiguously in the frame_header buffer. After cutting out
-the frame headers, the frame data is stored as a single block, consisting of the HPACK encoded
-HTTP/2 headers.
+The Http2StreamSplitter splits HTTP/2 traffic on frame boundaries with two exceptions. First, any
+Continuation frames following a Headers frame are aggregated and sent through detection as a single
+header block. Second, long Data frames are split into 16kb chunks for inspection as message bodies
+are in NHI. The Http2StreamSplitter strips the frame headers from the frame data and stores them in
+separate buffers. In the case of a Headers frame followed by Continuation frames, the Headers frame
+header is stored and the Continuation frame headers are discarded.
+
+HTTP/2 frames are stored in frame objects for processing.
+1. Http2Frame - top level class
+2. Http2HeadersFrame : Http2Frame - common elements of frames containing headers
+3. Htt2HeadersFrameWithStartline : Http2HeadersFrame - common elements of headers with start lines
+4. Http2HeadersFrameHeader : Htt2HeadersFrameWithStartline - regular request/response headers
+5. Http2PushPromiseFrame : Htt2HeadersFrameWithStartline - server push headers
+6. Http2HeadersFrameTrailer : Http2HeadersFrame - headers frame containing trailers
+7. Http2DataFrame : Http2Frame
+8. Http2SettingsFrame : Http2Frame
HTTP/2 headers frames can come at the start of a stream and contain the header block, or at the end
of a stream and contain trailers. H2I contains headers frame subclasses Http2HeadersFrameHeader and
-Http2HeadersFrameTrailer to support these two types of headers frames. Headers frames containing the
-header block will contain pseudo-headers that must be converted into an HTTP/1.1 start line in
-addition to regular HTTP/1.1 headers. The two Http2StartLine subclasses, Http2RequestLine and
-Http2ResponseLine perform this translation and generate the start-line, which is stored in a new
-buffer inside the Http2StartLine object. Trailers may only contain regular headers.
+Http2HeadersFrameTrailer to support these two types of headers frames. Http2PushPromise frames begin
+server pushed streams and also contain the header block. Headers frames containing the header block
+will contain pseudo-headers that must be converted into an HTTP/1.1 start line in addition to
+regular HTTP/1.1 headers. The two Http2StartLine subclasses, Http2RequestLine and Http2ResponseLine
+perform this translation and generate the start-line, which is stored in a new buffer inside the
+Http2StartLine object. Trailers may only contain regular headers.
Both headers and trailers must undergo HPACK decoding before being sent to NHI for processing. To
perform decoding, reassemble() makes a first copy of the encoded headers, which is stored in the
return get_stream(current_stream[source_id]);
}
+class Http2Stream* Http2FlowData::get_processing_stream()
+{
+ return get_stream(get_processing_stream_id());
+}
+
+uint32_t Http2FlowData::get_processing_stream_id() const
+{
+ return processing_stream_id;
+}
+
// processing stream is the current stream except for push promise frames with properly formatted
// promised stream IDs
-class Http2Stream* Http2FlowData::get_processing_stream(const HttpCommon::SourceId source_id)
+void Http2FlowData::set_processing_stream_id(const HttpCommon::SourceId source_id)
{
- if (processing_stream_id[source_id] == NO_STREAM_ID)
- {
- if (frame_type[source_id] == FT_PUSH_PROMISE)
- processing_stream_id[source_id] = Http2PushPromiseFrame::get_promised_stream_id(
- events[source_id], infractions[source_id], frame_data[source_id],
- frame_data_size[source_id]);
- if (processing_stream_id[source_id] == NO_STREAM_ID)
- processing_stream_id[source_id] = current_stream[source_id];
- }
- return get_stream(processing_stream_id[source_id]);
+ assert(processing_stream_id == NO_STREAM_ID);
+ if (frame_type[source_id] == FT_PUSH_PROMISE)
+ processing_stream_id = Http2PushPromiseFrame::get_promised_stream_id(
+ events[source_id], infractions[source_id], frame_data[source_id],
+ frame_data_size[source_id]);
+ if (processing_stream_id == NO_STREAM_ID)
+ processing_stream_id = current_stream[source_id];
}
-uint32_t Http2FlowData::get_current_stream_id(const HttpCommon::SourceId source_id)
+uint32_t Http2FlowData::get_current_stream_id(const HttpCommon::SourceId source_id) const
{
return current_stream[source_id];
}
friend class Http2HeadersFrame;
friend class Http2HeadersFrameHeader;
friend class Http2HeadersFrameTrailer;
+ friend class Http2HeadersFrameWithStartline;
friend class Http2Hpack;
friend class Http2Inspect;
friend class Http2PushPromiseFrame;
~StreamInfo() { delete stream; }
};
class Http2Stream* get_current_stream(const HttpCommon::SourceId source_id);
- uint32_t get_current_stream_id(const HttpCommon::SourceId source_id);
- class Http2Stream* get_processing_stream(const HttpCommon::SourceId source_id);
+ uint32_t get_current_stream_id(const HttpCommon::SourceId source_id) const;
+ class Http2Stream* get_processing_stream();
+ uint32_t get_processing_stream_id() const;
+ void set_processing_stream_id(const HttpCommon::SourceId source_id);
Http2HpackDecoder* get_hpack_decoder(const HttpCommon::SourceId source_id)
{ return &hpack_decoder[source_id]; }
// header). This is set in scan().
uint32_t current_stream[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID };
// Stream ID of the stream responsible for processing the current frame. This will be the same
- // as current_stream except when processing a push_promise frame. This is set in eval().
- uint32_t processing_stream_id[2] = { Http2Enums::NO_STREAM_ID, Http2Enums::NO_STREAM_ID };
+ // as current_stream except when processing a push_promise frame. This is set in eval() and
+ // cleared in clear().
+ uint32_t processing_stream_id = Http2Enums::NO_STREAM_ID;
// At any given time there may be different streams going in each direction. But only one of
// them is the stream that http_inspect is actually processing at the moment.
uint32_t stream_in_hi = Http2Enums::NO_STREAM_ID;
session_data->hi->clear(&dummy_pkt);
}
-void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow)
+void Http2HeadersFrame::process_decoded_headers(HttpFlowData* http_flow, SourceId hi_source_id)
{
if (session_data->abort_flow[source_id])
return;
dummy_pkt.flow = session_data->flow;
const uint32_t unused = 0;
const StreamSplitter::Status header_scan_result =
- session_data->hi_ss[source_id]->scan(&dummy_pkt, http1_header.start(),
+ session_data->hi_ss[hi_source_id]->scan(&dummy_pkt, http1_header.start(),
http1_header.length(), unused, &flush_offset);
assert(header_scan_result == StreamSplitter::FLUSH);
UNUSED(header_scan_result);
// http_inspect reassemble() of headers
{
unsigned copied;
- stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
+ stream_buf = session_data->hi_ss[hi_source_id]->reassemble(session_data->flow,
http1_header.length(), 0, http1_header.start(), http1_header.length(), PKT_PDU_TAIL,
copied);
assert(stream_buf.data != nullptr);
{
Http2DummyPacket dummy_pkt;
dummy_pkt.flow = session_data->flow;
- dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
+ dummy_pkt.packet_flags = (hi_source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
dummy_pkt.dsize = stream_buf.length;
dummy_pkt.data = stream_buf.data;
dummy_pkt.xtradata_mask = 0;
// Following if condition won't get exercised until finish() (during Headers) is
// implemented for H2I. Without finish() H2I will only flush complete header blocks. Below
// ABORT is only possible if tcp connection closes unexpectedly in middle of a header.
- if (http_flow->get_type_expected(source_id) == HttpEnums::SEC_ABORT)
+ if (http_flow->get_type_expected(hi_source_id) == HttpEnums::SEC_ABORT)
{
*session_data->infractions[source_id] += INF_INVALID_HEADER;
session_data->events[source_id]->create_event(EVENT_INVALID_HEADER);
Http2HeadersFrame(const uint8_t* header_buffer, const uint32_t header_len,
const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data,
HttpCommon::SourceId src_id, Http2Stream* stream);
- void process_decoded_headers(HttpFlowData* http_flow);
+ void process_decoded_headers(HttpFlowData* http_flow, HttpCommon::SourceId hi_source_id);
uint8_t* decoded_headers = nullptr; // working buffer to store decoded headers
Field http1_header; // finalized headers to be passed to NHI
#include "http2_headers_frame_header.h"
-#include "protocols/packet.h"
#include "service_inspectors/http_inspect/http_enum.h"
#include "service_inspectors/http_inspect/http_flow_data.h"
-#include "service_inspectors/http_inspect/http_inspect.h"
-#include "service_inspectors/http_inspect/http_stream_splitter.h"
-#include "http2_dummy_packet.h"
#include "http2_enum.h"
#include "http2_flow_data.h"
#include "http2_hpack.h"
#include "http2_request_line.h"
-#include "http2_start_line.h"
#include "http2_status_line.h"
#include "http2_stream.h"
Http2HeadersFrameHeader::Http2HeadersFrameHeader(const uint8_t* header_buffer,
const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len,
Http2FlowData* session_data_, HttpCommon::SourceId source_id_, Http2Stream* stream_) :
- Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_,
- stream_)
+ Http2HeadersFrameWithStartline(header_buffer, header_len, data_buffer, data_len, session_data_,
+ source_id_, stream_)
{
if (!process_frame)
return;
}
}
-Http2HeadersFrameHeader::~Http2HeadersFrameHeader()
-{
- delete start_line_generator;
-}
-
bool Http2HeadersFrameHeader::valid_sequence(Http2Enums::StreamState state)
{
return (state == Http2Enums::STREAM_EXPECT_HEADERS);
if (!process_frame)
return;
- // http_inspect scan() of start line
- {
- uint32_t flush_offset;
- Http2DummyPacket dummy_pkt;
- dummy_pkt.flow = session_data->flow;
- const uint32_t unused = 0;
- const StreamSplitter::Status start_scan_result =
- session_data->hi_ss[source_id]->scan(&dummy_pkt, start_line.start(),
- start_line.length(), unused, &flush_offset);
- assert(start_scan_result == StreamSplitter::FLUSH);
- UNUSED(start_scan_result);
- assert((int64_t)flush_offset == start_line.length());
- }
-
- StreamBuffer stream_buf;
-
- // http_inspect reassemble() of start line
- {
- unsigned copied;
- stream_buf = session_data->hi_ss[source_id]->reassemble(session_data->flow,
- start_line.length(), 0, start_line.start(), start_line.length(), PKT_PDU_TAIL,
- copied);
- assert(stream_buf.data != nullptr);
- assert(copied == (unsigned)start_line.length());
- }
+ HttpFlowData* http_flow;
+ if (!process_start_line(http_flow, source_id))
+ return;
- HttpFlowData* const http_flow =
- session_data->get_current_stream(source_id)->get_hi_flow_data();
- // http_inspect eval() and clear() of start line
- {
- Http2DummyPacket dummy_pkt;
- dummy_pkt.flow = session_data->flow;
- dummy_pkt.packet_flags = (source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
- dummy_pkt.dsize = stream_buf.length;
- dummy_pkt.data = stream_buf.data;
- session_data->hi->eval(&dummy_pkt);
- if (http_flow->get_type_expected(source_id) != HttpEnums::SEC_HEADER)
- {
- *session_data->infractions[source_id] += INF_INVALID_STARTLINE;
- session_data->events[source_id]->create_event(EVENT_INVALID_STARTLINE);
- stream->set_state(source_id, STREAM_ERROR);
- return;
- }
- session_data->hi->clear(&dummy_pkt);
- }
+ // if END_STREAM flag set on headers, tell http_inspect not to expect a message body
+ if (get_flags() & END_STREAM)
+ stream->get_hi_flow_data()->finish_h2_body(source_id, HttpEnums::H2_BODY_NO_BODY, false);
- process_decoded_headers(http_flow);
+ process_decoded_headers(http_flow, source_id);
}
void Http2HeadersFrameHeader::update_stream_state()
void Http2HeadersFrameHeader::print_frame(FILE* output)
{
fprintf(output, "Headers frame\n");
- start_line.print(output, "Decoded start-line");
- Http2HeadersFrame::print_frame(output);
+ Http2HeadersFrameWithStartline::print_frame(output);
}
#endif
#define HTTP2_HEADERS_FRAME_HEADER_H
#include "http2_frame.h"
-#include "http2_headers_frame.h"
+#include "http2_headers_frame_with_startline.h"
class Http2StartLine;
-class Http2HeadersFrameHeader : public Http2HeadersFrame
+class Http2HeadersFrameHeader : public Http2HeadersFrameWithStartline
{
public:
- ~Http2HeadersFrameHeader() override;
-
friend Http2Frame* Http2Frame::new_frame(const uint8_t*, const uint32_t, const uint8_t*,
const uint32_t, Http2FlowData*, HttpCommon::SourceId, Http2Stream* stream);
Http2HeadersFrameHeader(const uint8_t* header_buffer, const uint32_t header_len,
const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data,
HttpCommon::SourceId src_id, Http2Stream* stream);
-
- Http2StartLine* start_line_generator = nullptr;
- Field start_line;
};
#endif
if (!process_frame)
return;
- HttpFlowData* const http_flow =
- session_data->get_current_stream(source_id)->get_hi_flow_data();
+ HttpFlowData* const http_flow = stream->get_hi_flow_data();
assert(http_flow);
if (http_flow->get_type_expected(source_id) != HttpEnums::SEC_TRAILER)
session_data->hi->clear(&dummy_pkt);
}
- process_decoded_headers(http_flow);
+ process_decoded_headers(http_flow, source_id);
}
void Http2HeadersFrameTrailer::update_stream_state()
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_headers_frame_with_startline.cc author Katura Harvey <katharve@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "http2_headers_frame_with_startline.h"
+
+#include "protocols/packet.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http_inspect/http_flow_data.h"
+#include "service_inspectors/http_inspect/http_inspect.h"
+#include "service_inspectors/http_inspect/http_stream_splitter.h"
+
+#include "http2_dummy_packet.h"
+#include "http2_enum.h"
+#include "http2_flow_data.h"
+#include "http2_hpack.h"
+#include "http2_request_line.h"
+#include "http2_start_line.h"
+#include "http2_status_line.h"
+#include "http2_stream.h"
+
+using namespace snort;
+using namespace HttpCommon;
+using namespace Http2Enums;
+
+
+Http2HeadersFrameWithStartline::~Http2HeadersFrameWithStartline()
+{
+ delete start_line_generator;
+}
+
+bool Http2HeadersFrameWithStartline::process_start_line(HttpFlowData*& http_flow,
+ SourceId hi_source_id)
+{
+ if (session_data->abort_flow[source_id])
+ return false;
+
+ // http_inspect scan() of start line
+ {
+ uint32_t flush_offset;
+ Http2DummyPacket dummy_pkt;
+ dummy_pkt.flow = session_data->flow;
+ const uint32_t unused = 0;
+ const StreamSplitter::Status start_scan_result =
+ session_data->hi_ss[hi_source_id]->scan(&dummy_pkt, start_line.start(),
+ start_line.length(), unused, &flush_offset);
+ assert(start_scan_result == StreamSplitter::FLUSH);
+ UNUSED(start_scan_result);
+ assert((int64_t)flush_offset == start_line.length());
+ }
+
+ StreamBuffer stream_buf;
+
+ // http_inspect reassemble() of start line
+ {
+ unsigned copied;
+ stream_buf = session_data->hi_ss[hi_source_id]->reassemble(session_data->flow,
+ start_line.length(), 0, start_line.start(), start_line.length(), PKT_PDU_TAIL,
+ copied);
+ assert(stream_buf.data != nullptr);
+ assert(copied == (unsigned)start_line.length());
+ }
+
+ http_flow = stream->get_hi_flow_data();
+ assert(http_flow);
+ // http_inspect eval() and clear() of start line
+ {
+ Http2DummyPacket dummy_pkt;
+ dummy_pkt.flow = session_data->flow;
+ dummy_pkt.packet_flags = (hi_source_id == SRC_CLIENT) ? PKT_FROM_CLIENT : PKT_FROM_SERVER;
+ dummy_pkt.dsize = stream_buf.length;
+ dummy_pkt.data = stream_buf.data;
+ session_data->hi->eval(&dummy_pkt);
+ if (http_flow->get_type_expected(hi_source_id) != HttpEnums::SEC_HEADER)
+ {
+ *session_data->infractions[source_id] += INF_INVALID_STARTLINE;
+ session_data->events[source_id]->create_event(EVENT_INVALID_STARTLINE);
+ stream->set_state(hi_source_id, STREAM_ERROR);
+ return false;
+ }
+ session_data->hi->clear(&dummy_pkt);
+ }
+ return true;
+}
+
+
+#ifdef REG_TEST
+void Http2HeadersFrameWithStartline::print_frame(FILE* output)
+{
+ start_line.print(output, "Decoded start-line");
+ Http2HeadersFrame::print_frame(output);
+}
+#endif
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+// http2_headers_frame_with_startline.h author Katura Harvey <katharve@cisco.com>
+
+#ifndef HTTP2_HEADERS_FRAME_WITH_STARTLINE_H
+#define HTTP2_HEADERS_FRAME_WITH_STARTLINE_H
+
+#include "service_inspectors/http_inspect/http_common.h"
+
+#include "http2_enum.h"
+#include "http2_frame.h"
+#include "http2_headers_frame.h"
+
+class Field;
+class Http2Frame;
+class Http2Stream;
+class HttpFlowData;
+
+class Http2HeadersFrameWithStartline : public Http2HeadersFrame
+{
+public:
+ ~Http2HeadersFrameWithStartline() override;
+
+#ifdef REG_TEST
+ void print_frame(FILE* output) override;
+#endif
+
+protected:
+ Http2HeadersFrameWithStartline(const uint8_t* header_buffer, const uint32_t header_len,
+ const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data,
+ HttpCommon::SourceId src_id, Http2Stream* stream_) :
+ Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, ssn_data, src_id,
+ stream_) { }
+ bool process_start_line(HttpFlowData*& http_flow, HttpCommon::SourceId hi_source_id);
+
+ Http2StartLine* start_line_generator = nullptr;
+ Field start_line;
+};
+#endif
if (!session_data->frame_in_detection)
return false;
- const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
- Http2Stream* const stream = session_data->get_processing_stream(source_id);
+ Http2Stream* const stream = session_data->get_processing_stream();
const Field& buffer = stream->get_buf(id);
if (buffer.length() <= 0)
return false;
return;
}
- Http2Stream* stream = session_data->get_processing_stream(source_id);
- assert(session_data->processing_stream_id[source_id] != NO_STREAM_ID);
+ session_data->set_processing_stream_id(source_id);
+ Http2Stream* stream = session_data->get_processing_stream();
+ assert(session_data->processing_stream_id != NO_STREAM_ID);
assert(stream);
session_data->stream_in_hi = stream->get_stream_id();
session_data->frame_in_detection = false;
- const SourceId source_id = p->is_from_client() ? SRC_CLIENT : SRC_SERVER;
-
- Http2Stream* stream = session_data->get_processing_stream(source_id);
+ Http2Stream* stream = session_data->get_processing_stream();
stream->clear_frame();
session_data->stream_in_hi = NO_STREAM_ID;
- session_data->processing_stream_id[source_id] = NO_STREAM_ID;
+ session_data->processing_stream_id = NO_STREAM_ID;
}
#ifdef REG_TEST
#include "http2_push_promise_frame.h"
+#include "service_inspectors/http_inspect/http_enum.h"
+#include "service_inspectors/http_inspect/http_flow_data.h"
+
#include "http2_flow_data.h"
#include "http2_hpack.h"
#include "http2_request_line.h"
-#include "http2_start_line.h"
#include "http2_stream.h"
#include "http2_utils.h"
+using namespace snort;
using namespace HttpCommon;
using namespace Http2Enums;
Http2PushPromiseFrame::Http2PushPromiseFrame(const uint8_t* header_buffer,
const uint32_t header_len, const uint8_t* data_buffer, const uint32_t data_len,
Http2FlowData* session_data_, HttpCommon::SourceId source_id_, Http2Stream* stream_) :
- Http2HeadersFrame(header_buffer, header_len, data_buffer, data_len, session_data_, source_id_,
- stream_)
+ Http2HeadersFrameWithStartline(header_buffer, header_len, data_buffer, data_len, session_data_,
+ source_id_, stream_)
{
// If this was a short frame, it's being processed by the stream that sent it. We've already
// alerted
}
}
-Http2PushPromiseFrame::~Http2PushPromiseFrame()
-{
- delete start_line_generator;
-}
-
bool Http2PushPromiseFrame::valid_sequence(Http2Enums::StreamState)
{
if (data.length() < PROMISED_ID_LENGTH)
return true;
}
-// FIXIT-E current implementation for testing purposes only. Headers are not yet being sent to
-// http_inspect.
void Http2PushPromiseFrame::analyze_http1()
{
- if (session_data->abort_flow[source_id])
- return;
-
- detection_required = true;
-
if (!start_line_generator->generate_start_line(start_line))
{
// can't send request or push-promise headers to http_inspect, but response will still
return;
}
- http1_header = hpack_decoder->get_decoded_headers(decoded_headers);
+ HttpFlowData* http_flow;
+ if (!process_start_line(http_flow, SRC_CLIENT))
+ return;
+
+ // Push promise cannot have a message body
+ // FIXIT-E handle bad request lines and cases where a message body is implied
+ stream->get_hi_flow_data()->finish_h2_body(SRC_CLIENT, HttpEnums::H2_BODY_NO_BODY, false);
+
+ process_decoded_headers(http_flow, SRC_CLIENT);
}
void Http2PushPromiseFrame::update_stream_state()
void Http2PushPromiseFrame::print_frame(FILE* output)
{
fprintf(output, "Push_Promise frame\n");
- start_line.print(output, "Decoded start-line");
- Http2HeadersFrame::print_frame(output);
+ Http2HeadersFrameWithStartline::print_frame(output);
}
#endif
#include "http2_enum.h"
#include "http2_frame.h"
-#include "http2_headers_frame.h"
+#include "http2_headers_frame_with_startline.h"
class Field;
class Http2Frame;
using Http2EventGen = EventGen<Http2Enums::EVENT__MAX_VALUE, Http2Enums::EVENT__NONE,
Http2Enums::HTTP2_GID>;
-class Http2PushPromiseFrame : public Http2HeadersFrame
+class Http2PushPromiseFrame : public Http2HeadersFrameWithStartline
{
public:
- ~Http2PushPromiseFrame() override;
bool valid_sequence(Http2Enums::StreamState state) override;
void analyze_http1() override;
void update_stream_state() override;
const uint8_t* data_buffer, const uint32_t data_len, Http2FlowData* ssn_data,
HttpCommon::SourceId src_id, Http2Stream* stream);
static const int32_t PROMISED_ID_LENGTH = 4;
- Http2StartLine* start_line_generator = nullptr;
- Field start_line;
};
#endif
extern const bool is_print_char[256]; // printable includes SP, tab, CR, LF
extern const bool is_sp_comma[256];
-enum H2BodyState { H2_BODY_NOT_COMPLETE, H2_BODY_COMPLETE, H2_BODY_COMPLETE_EXPECT_TRAILERS };
+enum H2BodyState { H2_BODY_NOT_COMPLETE, H2_BODY_COMPLETE, H2_BODY_COMPLETE_EXPECT_TRAILERS,
+ H2_BODY_NO_BODY };
} // end namespace HttpEnums
void HttpMsgHeader::publish()
{
- const uint32_t stream_id = get_h2_stream_id(source_id);
+ const uint32_t stream_id = get_h2_stream_id();
HttpEvent http_event(this, session_data->for_http2, stream_id);
create_event(EVENT_BAD_CONTENT_LENGTH);
}
}
- session_data->type_expected[source_id] = SEC_BODY_H2;
- prepare_body();
+ if (session_data->h2_body_state[source_id] == H2_BODY_NO_BODY)
+ session_data->half_reset(source_id);
+ else
+ {
+ session_data->type_expected[source_id] = SEC_BODY_H2;
+ prepare_body();
+ }
return;
}
}
// Generate the unique file id for multi file processing
- set_multi_file_processing_id(get_transaction_id(), get_h2_stream_id(source_id));
+ set_multi_file_processing_id(get_transaction_id(), get_h2_stream_id());
// Do we meet all the conditions for MIME file processing?
if (source_id == SRC_CLIENT)
trailer[SRC_SERVER] = transaction->get_trailer(SRC_SERVER);
}
-uint32_t HttpMsgSection::get_h2_stream_id(HttpCommon::SourceId source_id)
+uint32_t HttpMsgSection::get_h2_stream_id()
{
if (h2_stream_id != STAT_NOT_COMPUTE)
return h2_stream_id;
+
+ h2_stream_id = 0;
if (session_data->for_http2)
{
- Http2FlowData* h2i_flow_data = (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
+ Http2FlowData* h2i_flow_data =
+ (Http2FlowData*)flow->get_flow_data(Http2FlowData::inspector_id);
assert(h2i_flow_data);
- h2_stream_id = h2i_flow_data->get_current_stream_id(source_id);
+ if (h2i_flow_data)
+ h2_stream_id = h2i_flow_data->get_processing_stream_id();
}
- else
- h2_stream_id = 0;
return h2_stream_id;
}
const bool tcp_close;
int64_t h2_stream_id = HttpCommon::STAT_NOT_COMPUTE;
- uint32_t get_h2_stream_id(HttpCommon::SourceId source_id);
+ uint32_t get_h2_stream_id();
// Pointers to related message sections in the same transaction
HttpMsgRequest* request;