http2: log as http to abstract http and http2 a little

This commit logs http2 as an http event. The idea is to somewhat
normalize http/http2 so common info can be version agnostic.

This puts the http2 specific fields in an "http2" object inside
the "http" object.

HTTP2 headers/values that are in common with HTTP1 are logged
under the "http" object to be compatible with HTTP1 logging.
pull/5282/head
Jason Ish 5 years ago committed by Victor Julien
parent 6a55606adb
commit f26d6eaf98

@ -19,9 +19,21 @@ use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction};
use super::parser;
use crate::jsonbuilder::{JsonBuilder, JsonError};
use std;
use std::collections::HashMap;
fn log_http2_headers(
blocks: &Vec<parser::HTTP2FrameHeaderBlock>, js: &mut JsonBuilder,
#[derive(Hash, PartialEq, Eq)]
enum HeaderName {
Method,
Path,
Host,
UserAgent,
Status,
ContentLength,
}
fn log_http2_headers<'a>(
blocks: &'a Vec<parser::HTTP2FrameHeaderBlock>, js: &mut JsonBuilder,
common: &mut HashMap<HeaderName, &'a Vec<u8>>,
) -> Result<(), JsonError> {
for j in 0..blocks.len() {
js.start_object()?;
@ -29,6 +41,29 @@ fn log_http2_headers(
parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => {
js.set_string_from_bytes("name", &blocks[j].name)?;
js.set_string_from_bytes("value", &blocks[j].value)?;
if let Ok(name) = std::str::from_utf8(&blocks[j].name) {
match name.to_lowercase().as_ref() {
":method" => {
common.insert(HeaderName::Method, &blocks[j].value);
}
":path" => {
common.insert(HeaderName::Path, &blocks[j].value);
}
":status" => {
common.insert(HeaderName::Status, &blocks[j].value);
}
"user-agent" => {
common.insert(HeaderName::UserAgent, &blocks[j].value);
}
"host" => {
common.insert(HeaderName::Host, &blocks[j].value);
}
"content-length" => {
common.insert(HeaderName::ContentLength, &blocks[j].value);
}
_ => {}
}
}
}
parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => {
js.set_uint("table_size_update", blocks[j].sizeupdate)?;
@ -42,20 +77,23 @@ fn log_http2_headers(
return Ok(());
}
fn log_headers(frames: &Vec<HTTP2Frame>, js: &mut JsonBuilder) -> Result<bool, JsonError> {
fn log_headers<'a>(
frames: &'a Vec<HTTP2Frame>, js: &mut JsonBuilder,
common: &mut HashMap<HeaderName, &'a Vec<u8>>,
) -> Result<bool, JsonError> {
let mut has_headers = false;
for frame in frames {
match &frame.data {
HTTP2FrameTypeData::HEADERS(hd) => {
log_http2_headers(&hd.blocks, js)?;
log_http2_headers(&hd.blocks, js, common)?;
has_headers = true;
}
HTTP2FrameTypeData::PUSHPROMISE(hd) => {
log_http2_headers(&hd.blocks, js)?;
log_http2_headers(&hd.blocks, js, common)?;
has_headers = true;
}
HTTP2FrameTypeData::CONTINUATION(hd) => {
log_http2_headers(&hd.blocks, js)?;
log_http2_headers(&hd.blocks, js, common)?;
has_headers = true;
}
_ => {}
@ -154,12 +192,16 @@ fn log_http2_frames(frames: &Vec<HTTP2Frame>, js: &mut JsonBuilder) -> Result<bo
}
fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonError> {
js.set_string("version", "2")?;
let mut common: HashMap<HeaderName, &Vec<u8>> = HashMap::new();
let mut has_headers = false;
// Request headers.
let mark = js.get_mark();
js.open_array("request_headers")?;
if log_headers(&tx.frames_ts, js)? {
if log_headers(&tx.frames_ts, js, &mut common)? {
js.close()?;
has_headers = true;
} else {
@ -169,13 +211,47 @@ fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonEr
// Response headers.
let mark = js.get_mark();
js.open_array("response_headers")?;
if log_headers(&tx.frames_tc, js)? {
if log_headers(&tx.frames_tc, js, &mut common)? {
js.close()?;
has_headers = true;
} else {
js.restore_mark(&mark)?;
}
for (name, value) in common {
match name {
HeaderName::Method => {
js.set_string_from_bytes("http_method", value)?;
}
HeaderName::Path => {
js.set_string_from_bytes("url", value)?;
}
HeaderName::Host => {
js.set_string_from_bytes("hostname", value)?;
}
HeaderName::UserAgent => {
js.set_string_from_bytes("http_user_agent", value)?;
}
HeaderName::ContentLength => {
if let Ok(value) = std::str::from_utf8(value) {
if let Ok(value) = value.parse::<u64>() {
js.set_uint("length", value)?;
}
}
}
HeaderName::Status => {
if let Ok(value) = std::str::from_utf8(value) {
if let Ok(value) = value.parse::<u64>() {
js.set_uint("status", value)?;
}
}
}
}
}
// The rest of http2 logging is placed in an "http2" object.
js.open_object("http2")?;
js.set_uint("stream_id", tx.stream_id as u64)?;
js.open_object("request")?;
let has_request = log_http2_frames(&tx.frames_ts, js)?;
@ -185,6 +261,9 @@ fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonEr
let has_response = log_http2_frames(&tx.frames_tc, js)?;
js.close()?;
// Close http2.
js.close()?;
return Ok(has_request || has_response || has_headers);
}

@ -169,7 +169,7 @@ static void AlertJsonHttp2(const Flow *f, const uint64_t tx_id, JsonBuilder *js)
void *tx_ptr = rs_http2_state_get_tx(h2_state, tx_id);
JsonBuilderMark mark = { 0, 0, 0 };
jb_get_mark(js, &mark);
jb_open_object(js, "http2");
jb_open_object(js, "http");
if (rs_http2_log_json(tx_ptr, js)) {
jb_close(js);
} else {

@ -87,7 +87,7 @@ static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p,
return 0;
}
JsonBuilder *js = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, "http2", NULL, tx_id);
JsonBuilder *js = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, "http", NULL, tx_id);
if (unlikely(js == NULL))
return 0;
@ -96,7 +96,7 @@ static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p,
/* reset */
MemBufferReset(aft->buffer);
jb_open_object(js, "http2");
jb_open_object(js, "http");
if (!rs_http2_log_json(txptr, js)) {
goto end;
}

Loading…
Cancel
Save