• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.12
/rust/src/http2/logger.rs
1
/* Copyright (C) 2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
use super::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction};
19
use super::parser;
20
use crate::detect::EnumString;
21
use crate::jsonbuilder::{JsonBuilder, JsonError};
22
use std;
23
use std::collections::{HashMap, HashSet};
24
use std::rc::Rc;
25

26
#[derive(Hash, PartialEq, Eq, Debug)]
27
enum HeaderName {
28
    Method,
29
    Path,
30
    Host,
31
    UserAgent,
32
    Status,
33
    ContentLength,
34
}
35

36
fn log_http2_headers<'a>(
11,129✔
37
    blocks: &'a [parser::HTTP2FrameHeaderBlock], js: &mut JsonBuilder,
11,129✔
38
    common: &mut HashMap<HeaderName, &'a Vec<u8>>,
11,129✔
39
) -> Result<(), JsonError> {
11,129✔
40
    let mut logged_headers = HashSet::new();
11,129✔
41
    for block in blocks {
115,627✔
42
        // delay js.start_object() because we skip suplicate headers
43
        match block.error {
104,498✔
44
            parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => {
45
                if Rc::strong_count(&block.name) > 2 {
97,740✔
46
                    // more than one reference in headers table + current headers
47
                    let ptr = Rc::as_ptr(&block.name) as usize;
39,399✔
48
                    if !logged_headers.insert(ptr) {
39,399✔
49
                        // only log once
50
                        continue;
98✔
51
                    }
39,301✔
52
                }
58,341✔
53
                js.start_object()?;
97,642✔
54
                js.set_string_from_bytes("name", &block.name)?;
97,642✔
55
                js.set_string_from_bytes("value", &block.value)?;
97,642✔
56
                if let Ok(name) = std::str::from_utf8(&block.name) {
97,642✔
57
                    match name.to_lowercase().as_ref() {
97,557✔
58
                        ":method" => {
97,557✔
59
                            common.insert(HeaderName::Method, &block.value);
5,184✔
60
                        }
5,184✔
61
                        ":path" => {
92,373✔
62
                            common.insert(HeaderName::Path, &block.value);
5,167✔
63
                        }
5,167✔
64
                        ":status" => {
87,206✔
65
                            common.insert(HeaderName::Status, &block.value);
4,626✔
66
                        }
4,626✔
67
                        "user-agent" => {
82,580✔
68
                            common.insert(HeaderName::UserAgent, &block.value);
4,807✔
69
                        }
4,807✔
70
                        "host" => {
77,773✔
71
                            common.insert(HeaderName::Host, &block.value);
5✔
72
                        }
5✔
73
                        "content-length" => {
77,768✔
74
                            common.insert(HeaderName::ContentLength, &block.value);
7,051✔
75
                        }
7,051✔
76
                        _ => {}
70,717✔
77
                    }
78
                }
85✔
79
            }
80
            parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => {
81
                js.start_object()?;
720✔
82
                js.set_uint("table_size_update", block.sizeupdate)?;
720✔
83
            }
84
            _ => {
85
                js.start_object()?;
6,038✔
86
                js.set_string("error", &block.error.to_string())?;
6,038✔
87
            }
88
        }
89
        js.close()?;
104,400✔
90
    }
91
    return Ok(());
11,129✔
92
}
11,129✔
93

94
fn log_headers<'a>(
12,166✔
95
    frames: &'a Vec<HTTP2Frame>, js: &mut JsonBuilder,
12,166✔
96
    common: &mut HashMap<HeaderName, &'a Vec<u8>>,
12,166✔
97
) -> Result<bool, JsonError> {
12,166✔
98
    let mut has_headers = false;
12,166✔
99
    for frame in frames {
35,777✔
100
        match &frame.data {
23,611✔
101
            HTTP2FrameTypeData::HEADERS(hd) => {
11,124✔
102
                log_http2_headers(&hd.blocks, js, common)?;
11,124✔
103
                has_headers = true;
11,124✔
104
            }
105
            HTTP2FrameTypeData::PUSHPROMISE(hd) => {
×
106
                log_http2_headers(&hd.blocks, js, common)?;
×
107
                has_headers = true;
×
108
            }
109
            HTTP2FrameTypeData::CONTINUATION(hd) => {
5✔
110
                log_http2_headers(&hd.blocks, js, common)?;
5✔
111
                has_headers = true;
5✔
112
            }
113
            _ => {}
12,482✔
114
        }
115
    }
116
    Ok(has_headers)
12,166✔
117
}
12,166✔
118

119
fn log_http2_frames(frames: &[HTTP2Frame], js: &mut JsonBuilder) -> Result<bool, JsonError> {
12,166✔
120
    let mut has_settings = false;
12,166✔
121
    for frame in frames {
35,777✔
122
        if let HTTP2FrameTypeData::SETTINGS(set) = &frame.data {
23,611✔
123
            if !has_settings {
333✔
124
                js.open_array("settings")?;
333✔
125
                has_settings = true;
333✔
126
            }
×
127
            for e in set {
1,271✔
128
                js.start_object()?;
938✔
129
                js.set_string(
938✔
130
                    "settings_id",
938✔
131
                    &format!("SETTINGS{}", &e.id.to_string().to_uppercase()),
938✔
132
                )?;
938✔
133
                js.set_uint("settings_value", e.value as u64)?;
938✔
134
                js.close()?;
938✔
135
            }
136
        }
23,278✔
137
    }
138
    if has_settings {
12,166✔
139
        js.close()?;
333✔
140
    }
11,833✔
141

142
    let mut has_error_code = false;
12,166✔
143
    let mut has_priority = false;
12,166✔
144
    let mut has_multiple = false;
12,166✔
145
    for frame in frames {
35,777✔
146
        match &frame.data {
23,611✔
147
            HTTP2FrameTypeData::GOAWAY(goaway) => {
4✔
148
                if !has_error_code {
4✔
149
                    if let Some(errcode) = parser::HTTP2ErrorCode::from_u(goaway.errorcode) {
4✔
150
                        js.set_string("error_code", errcode.to_str())?;
4✔
151
                    } else {
152
                        js.set_string("error_code", &format!("unknown-{}", goaway.errorcode))?;
×
153
                    }
154
                    has_error_code = true;
4✔
155
                } else if !has_multiple {
×
156
                    js.set_string("has_multiple", "error_code")?;
×
157
                    has_multiple = true;
×
158
                }
×
159
            }
160
            HTTP2FrameTypeData::RSTSTREAM(rst) => {
72✔
161
                if !has_error_code {
72✔
162
                    if let Some(errcode) = parser::HTTP2ErrorCode::from_u(rst.errorcode) {
72✔
163
                        js.set_string("error_code", errcode.to_str())?;
69✔
164
                    } else {
165
                        js.set_string("error_code", &format!("unknown-{}", rst.errorcode))?;
3✔
166
                    }
167
                    has_error_code = true;
72✔
UNCOV
168
                } else if !has_multiple {
×
UNCOV
169
                    js.set_string("has_multiple", "error_code")?;
×
UNCOV
170
                    has_multiple = true;
×
UNCOV
171
                }
×
172
            }
173
            HTTP2FrameTypeData::PRIORITY(priority) => {
9✔
174
                if !has_priority {
9✔
175
                    js.set_uint("priority", priority.weight as u64)?;
9✔
176
                    has_priority = true;
9✔
177
                } else if !has_multiple {
×
178
                    js.set_string("has_multiple", "priority")?;
×
179
                    has_multiple = true;
×
180
                }
×
181
            }
182
            HTTP2FrameTypeData::HEADERS(hd) => {
11,124✔
183
                if let Some(ref priority) = hd.priority {
11,124✔
184
                    if !has_priority {
4,607✔
185
                        js.set_uint("priority", priority.weight as u64)?;
4,606✔
186
                        has_priority = true;
4,606✔
187
                    } else if !has_multiple {
1✔
188
                        js.set_string("has_multiple", "priority")?;
1✔
189
                        has_multiple = true;
1✔
190
                    }
×
191
                }
6,517✔
192
            }
193
            _ => {}
12,402✔
194
        }
195
    }
196
    return Ok(has_settings || has_error_code || has_priority);
12,166✔
197
}
12,166✔
198

199
fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonError> {
6,083✔
200
    js.open_object("http")?;
6,083✔
201
    js.set_string("version", "2")?;
6,083✔
202

203
    let mut common: HashMap<HeaderName, &Vec<u8>> = HashMap::new();
6,083✔
204

6,083✔
205
    let mut has_headers = false;
6,083✔
206

6,083✔
207
    // Request headers.
6,083✔
208
    let mark = js.get_mark();
6,083✔
209
    js.open_array("request_headers")?;
6,083✔
210
    if log_headers(&tx.frames_ts, js, &mut common)? {
6,083✔
211
        js.close()?;
5,092✔
212
        has_headers = true;
5,092✔
213
    } else {
214
        js.restore_mark(&mark)?;
991✔
215
    }
216

217
    // Response headers.
218
    let mark = js.get_mark();
6,083✔
219
    js.open_array("response_headers")?;
6,083✔
220
    if log_headers(&tx.frames_tc, js, &mut common)? {
6,083✔
221
        js.close()?;
3,730✔
222
        has_headers = true;
3,730✔
223
    } else {
224
        js.restore_mark(&mark)?;
2,353✔
225
    }
226

227
    for (name, value) in common {
29,020✔
228
        match name {
22,937✔
229
            HeaderName::Method => {
230
                js.set_string_from_bytes("http_method", value)?;
5,079✔
231
            }
232
            HeaderName::Path => {
233
                js.set_string_from_bytes("url", value)?;
5,087✔
234
            }
235
            HeaderName::Host => {
236
                js.set_string_from_bytes("hostname", value)?;
5✔
237
            }
238
            HeaderName::UserAgent => {
239
                js.set_string_from_bytes("http_user_agent", value)?;
4,764✔
240
            }
241
            HeaderName::ContentLength => {
242
                if let Ok(value) = std::str::from_utf8(value) {
4,257✔
243
                    if let Ok(value) = value.parse::<u64>() {
4,257✔
244
                        js.set_uint("length", value)?;
4,232✔
245
                    }
25✔
246
                }
×
247
            }
248
            HeaderName::Status => {
249
                if let Ok(value) = std::str::from_utf8(value) {
3,745✔
250
                    if let Ok(value) = value.parse::<u64>() {
3,726✔
251
                        js.set_uint("status", value)?;
3,686✔
252
                    }
40✔
253
                }
19✔
254
            }
255
        }
256
    }
257

258
    // The rest of http2 logging is placed in an "http2" object.
259
    js.open_object("http2")?;
6,083✔
260

261
    js.set_uint("stream_id", tx.stream_id as u64)?;
6,083✔
262
    let mark = js.get_mark();
6,083✔
263
    js.open_object("request")?;
6,083✔
264
    let has_request = log_http2_frames(&tx.frames_ts, js)?;
6,083✔
265
    if has_request {
6,083✔
266
        js.close()?;
4,816✔
267
    } else {
268
        js.restore_mark(&mark)?;
1,267✔
269
    }
270

271
    let mark = js.get_mark();
6,083✔
272
    js.open_object("response")?;
6,083✔
273
    let has_response = log_http2_frames(&tx.frames_tc, js)?;
6,083✔
274
    if has_response {
6,083✔
275
        js.close()?;
202✔
276
    } else {
277
        js.restore_mark(&mark)?;
5,881✔
278
    }
279

280
    js.close()?; // http2
6,083✔
281
    js.close()?; // http
6,083✔
282

283
    return Ok(has_request || has_response || has_headers);
6,083✔
284
}
6,083✔
285

286
#[no_mangle]
287
pub unsafe extern "C" fn SCHttp2LogJson(
6,083✔
288
    tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
6,083✔
289
) -> bool {
6,083✔
290
    let tx = cast_pointer!(tx, HTTP2Transaction);
6,083✔
291
    if let Ok(x) = log_http2(tx, js) {
6,083✔
292
        return x;
6,083✔
293
    }
×
294
    return false;
×
295
}
6,083✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc