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

OISF / suricata / 22553492142

01 Mar 2026 09:48PM UTC coverage: 70.74% (-2.9%) from 73.687%
22553492142

Pull #14920

github

web-flow
Merge e15a765bc into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

38209 of 77306 branches covered (49.43%)

Branch coverage included in aggregate %.

533 of 779 new or added lines in 5 files covered. (68.42%)

11924 existing lines in 491 files now uncovered.

252429 of 333548 relevant lines covered (75.68%)

2403268.06 hits per line

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

82.45
/rust/src/http2/parser.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::huffman;
19
use crate::common::nom7::bits;
20
use crate::detect::uint::{detect_parse_uint, DetectUintData};
21
use crate::http2::http2::{HTTP2DynTable, HTTP2_MAX_TABLESIZE};
22
use nom7::bits::streaming::take as take_bits;
23
use nom7::bytes::complete::tag;
24
use nom7::bytes::streaming::{take, take_while};
25
use nom7::combinator::{complete, cond, map_opt, verify};
26
use nom7::error::{make_error, ErrorKind};
27
use nom7::multi::many0;
28
use nom7::number::streaming::{be_u16, be_u24, be_u32, be_u8};
29
use nom7::sequence::tuple;
30
use nom7::{Err, IResult};
31
use std::fmt;
32
use std::str::FromStr;
33
use std::rc::Rc;
34
use base64::{Engine, engine::general_purpose::STANDARD_NO_PAD};
35

36
#[repr(u8)]
37
#[derive(EnumStringU8)]
38
#[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
1,376✔
39
// parse GOAWAY, not GO_AWAY
40
#[suricata(enum_string_style = "UPPERCASE")]
41
pub enum HTTP2FrameType {
42
    Data = 0,
43
    Headers = 1,
44
    Priority = 2,
45
    RstStream = 3,
46
    Settings = 4,
47
    PushPromise = 5,
48
    Ping = 6,
49
    GoAway = 7,
50
    WindowUpdate = 8,
51
    Continuation = 9,
52
}
53

54
#[derive(PartialEq, Eq, Debug)]
55
pub struct HTTP2FrameHeader {
56
    //we could add detection on (GOAWAY) additional data
57
    pub length: u32,
58
    pub ftype: u8,
59
    pub flags: u8,
60
    pub reserved: u8,
61
    pub stream_id: u32,
62
}
63

64
pub fn http2_parse_frame_header(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeader> {
1,517✔
65
    let (i, length) = be_u24(i)?;
1,517✔
66
    let (i, ftype) = be_u8(i)?;
1,517✔
67
    let (i, flags) = be_u8(i)?;
1,517✔
68
    let (i, b) = be_u32(i)?;
1,517✔
69
    let (reserved, stream_id) = ((b >> 31) as u8, b & 0x7fff_ffff);
1,517✔
70
    Ok((
1,517✔
71
        i,
1,517✔
72
        HTTP2FrameHeader {
1,517✔
73
            length,
1,517✔
74
            ftype,
1,517✔
75
            flags,
1,517✔
76
            reserved,
1,517✔
77
            stream_id,
1,517✔
78
        },
1,517✔
79
    ))
1,517✔
80
}
1,517✔
81

82
#[repr(u32)]
83
#[derive(EnumStringU32)]
84
#[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
×
85
#[suricata(enum_string_style = "LOG_UPPERCASE")]
86
pub enum HTTP2ErrorCode {
87
    NoError = 0,
88
    ProtocolError = 1,
89
    InternalError = 2,
90
    FlowControlError = 3,
91
    SettingsTimeout = 4,
92
    StreamClosed = 5,
93
    FrameSizeError = 6,
94
    RefusedStream = 7,
95
    Cancel = 8,
96
    CompressionError = 9,
97
    ConnectError = 10,
98
    EnhanceYourCalm = 11,
99
    InadequateSecurity = 12,
100
    Http11Required = 13,
101
}
102

103
#[derive(Clone, Copy, Debug)]
104
pub struct HTTP2FrameGoAway {
105
    pub errorcode: u32, //HTTP2ErrorCode
106
}
107

108
pub fn http2_parse_frame_goaway(i: &[u8]) -> IResult<&[u8], HTTP2FrameGoAway> {
11✔
109
    let (i, _last_stream_id) = be_u32(i)?;
11✔
110
    let (i, errorcode) = be_u32(i)?;
11✔
111
    Ok((i, HTTP2FrameGoAway { errorcode }))
11✔
112
}
11✔
113

114
#[derive(Clone, Copy, Debug)]
115
pub struct HTTP2FrameRstStream {
116
    pub errorcode: u32, ////HTTP2ErrorCode
117
}
118

119
pub fn http2_parse_frame_rststream(i: &[u8]) -> IResult<&[u8], HTTP2FrameRstStream> {
21✔
120
    let (i, errorcode) = be_u32(i)?;
21✔
121
    Ok((i, HTTP2FrameRstStream { errorcode }))
21✔
122
}
21✔
123

124
#[derive(Clone, Copy, Debug)]
125
pub struct HTTP2FramePriority {
126
    pub exclusive: u8,
127
    pub dependency: u32,
128
    pub weight: u8,
129
}
130

131
pub fn http2_parse_frame_priority(i: &[u8]) -> IResult<&[u8], HTTP2FramePriority> {
6✔
132
    let (i, b) = be_u32(i)?;
6✔
133
    let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff);
6✔
134
    let (i, weight) = be_u8(i)?;
6✔
135
    Ok((
6✔
136
        i,
6✔
137
        HTTP2FramePriority {
6✔
138
            exclusive,
6✔
139
            dependency,
6✔
140
            weight,
6✔
141
        },
6✔
142
    ))
6✔
143
}
6✔
144

145
#[derive(Clone, Copy, Debug)]
146
pub struct HTTP2FrameWindowUpdate {
147
    pub reserved: u8,
148
    pub sizeinc: u32,
149
}
150

151
pub fn http2_parse_frame_windowupdate(i: &[u8]) -> IResult<&[u8], HTTP2FrameWindowUpdate> {
177✔
152
    let (i, b) = be_u32(i)?;
177✔
153
    let (reserved, sizeinc) = ((b >> 31) as u8, b & 0x7fff_ffff);
177✔
154
    Ok((i, HTTP2FrameWindowUpdate { reserved, sizeinc }))
177✔
155
}
177✔
156

157
#[derive(Clone, Copy, Debug)]
158
pub struct HTTP2FrameHeadersPriority {
159
    pub exclusive: u8,
160
    pub dependency: u32,
161
    pub weight: u8,
162
}
163

164
pub fn http2_parse_headers_priority(i: &[u8]) -> IResult<&[u8], HTTP2FrameHeadersPriority> {
77✔
165
    let (i, b) = be_u32(i)?;
77✔
166
    let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff);
77✔
167
    let (i, weight) = be_u8(i)?;
77✔
168
    Ok((
77✔
169
        i,
77✔
170
        HTTP2FrameHeadersPriority {
77✔
171
            exclusive,
77✔
172
            dependency,
77✔
173
            weight,
77✔
174
        },
77✔
175
    ))
77✔
176
}
77✔
177

178
pub const HTTP2_STATIC_HEADERS_NUMBER: usize = 61;
179

180
fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP2FrameHeaderBlock> {
1,860✔
181
    let (name, value) = match n {
1,860✔
182
        1 => (":authority", ""),
25✔
183
        2 => (":method", "GET"),
55✔
184
        3 => (":method", "POST"),
42✔
185
        4 => (":path", "/"),
61✔
186
        5 => (":path", "/index.html"),
36✔
187
        6 => (":scheme", "http"),
46✔
188
        7 => (":scheme", "https"),
50✔
189
        8 => (":status", "200"),
92✔
UNCOV
190
        9 => (":status", "204"),
×
191
        10 => (":status", "206"),
9✔
UNCOV
192
        11 => (":status", "304"),
×
193
        12 => (":status", "400"),
1✔
194
        13 => (":status", "404"),
7✔
195
        14 => (":status", "500"),
2✔
UNCOV
196
        15 => ("accept-charset", ""),
×
197
        16 => ("accept-encoding", "gzip, deflate"),
46✔
198
        17 => ("accept-language", ""),
6✔
199
        18 => ("accept-ranges", ""),
15✔
200
        19 => ("accept", ""),
23✔
201
        20 => ("access-control-allow-origin", ""),
5✔
202
        21 => ("age", ""),
3✔
UNCOV
203
        22 => ("allow", ""),
×
UNCOV
204
        23 => ("authorization", ""),
×
205
        24 => ("cache-control", ""),
49✔
UNCOV
206
        25 => ("content-disposition", ""),
×
207
        26 => ("content-encoding", ""),
6✔
UNCOV
208
        27 => ("content-language", ""),
×
209
        28 => ("content-length", ""),
74✔
UNCOV
210
        29 => ("content-location", ""),
×
211
        30 => ("content-range", ""),
9✔
212
        31 => ("content-type", ""),
43✔
213
        32 => ("cookie", ""),
7✔
214
        33 => ("date", ""),
61✔
215
        34 => ("etag", ""),
5✔
UNCOV
216
        35 => ("expect", ""),
×
217
        36 => ("expires", ""),
6✔
UNCOV
218
        37 => ("from", ""),
×
219
        38 => ("host", ""),
2✔
UNCOV
220
        39 => ("if-match", ""),
×
UNCOV
221
        40 => ("if-modified-since", ""),
×
UNCOV
222
        41 => ("if-none-match", ""),
×
UNCOV
223
        42 => ("if-range", ""),
×
UNCOV
224
        43 => ("if-unmodified-since", ""),
×
225
        44 => ("last-modified", ""),
46✔
UNCOV
226
        45 => ("link", ""),
×
UNCOV
227
        46 => ("location", ""),
×
UNCOV
228
        47 => ("max-forwards", ""),
×
UNCOV
229
        48 => ("proxy-authenticate", ""),
×
UNCOV
230
        49 => ("proxy-authorization", ""),
×
UNCOV
231
        50 => ("range", ""),
×
232
        51 => ("referer", ""),
5✔
UNCOV
233
        52 => ("refresh", ""),
×
UNCOV
234
        53 => ("retry-after", ""),
×
235
        54 => ("server", ""),
28✔
236
        55 => ("set-cookie", ""),
1✔
237
        56 => ("strict-transport-security", ""),
3✔
UNCOV
238
        57 => ("transfer-encoding", ""),
×
239
        58 => ("user-agent", ""),
23✔
240
        59 => ("vary", ""),
9✔
241
        60 => ("via", ""),
5✔
UNCOV
242
        61 => ("www-authenticate", ""),
×
243
        _ => ("", ""),
954✔
244
    };
245
    if !name.is_empty() {
1,860✔
246
        return Some(HTTP2FrameHeaderBlock {
906✔
247
            name: Rc::new(name.as_bytes().to_vec()),
906✔
248
            value: Rc::new(value.as_bytes().to_vec()),
906✔
249
            error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
906✔
250
            sizeupdate: 0,
906✔
251
        });
906✔
252
    } else {
253
        //use dynamic table
254
        if n == 0 {
954✔
255
            return Some(HTTP2FrameHeaderBlock {
1✔
256
                name: Rc::new(Vec::new()),
1✔
257
                value: Rc::new(Vec::new()),
1✔
258
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0,
1✔
259
                sizeupdate: 0,
1✔
260
            });
1✔
261
        } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize {
953✔
UNCOV
262
            return Some(HTTP2FrameHeaderBlock {
×
UNCOV
263
                name: Rc::new(Vec::new()),
×
UNCOV
264
                value: Rc::new(Vec::new()),
×
UNCOV
265
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed,
×
UNCOV
266
                sizeupdate: 0,
×
UNCOV
267
            });
×
268
        } else {
269
            let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER);
953✔
270
            let headcopy = HTTP2FrameHeaderBlock {
953✔
271
                name: dyn_headers.table[indyn].name.clone(),
953✔
272
                value: dyn_headers.table[indyn].value.clone(),
953✔
273
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
953✔
274
                sizeupdate: 0,
953✔
275
            };
953✔
276
            return Some(headcopy);
953✔
277
        }
278
    }
279
}
1,860✔
280

281
#[repr(u8)]
282
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
283
pub enum HTTP2HeaderDecodeStatus {
284
    HTTP2HeaderDecodeSuccess = 0,
285
    HTTP2HeaderDecodeSizeUpdate = 1,
286
    HTTP2HeaderDecodeError = 0x80,
287
    HTTP2HeaderDecodeNotIndexed = 0x81,
288
    HTTP2HeaderDecodeIntegerOverflow = 0x82,
289
    HTTP2HeaderDecodeIndex0 = 0x83,
290
}
291

292
impl fmt::Display for HTTP2HeaderDecodeStatus {
UNCOV
293
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
UNCOV
294
        write!(f, "{:?}", self)
×
UNCOV
295
    }
×
296
}
297

298
#[derive(Clone, Debug)]
299
pub struct HTTP2FrameHeaderBlock {
300
    // Use Rc reference counted so that indexed headers do not get copied.
301
    // Otherwise, this leads to quadratic complexity in memory occupation.
302
    pub name: Rc<Vec<u8>>,
303
    pub value: Rc<Vec<u8>>,
304
    pub error: HTTP2HeaderDecodeStatus,
305
    pub sizeupdate: u64,
306
}
307

308
fn http2_parse_headers_block_indexed<'a>(
1,235✔
309
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
1,235✔
310
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,235✔
311
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
1,235✔
312
        bits(complete(tuple((
1,235✔
313
            verify(take_bits(1u8), |&x| x == 1),
1,235✔
314
            take_bits(7u8),
1,235✔
315
        ))))(input)
1,235✔
316
    }
1,235✔
317
    let (i2, indexed) = parser(input)?;
1,235✔
318
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x7F)?;
1,235✔
319
    match http2_frame_header_static(indexreal, dyn_headers) {
1,235✔
320
        Some(h) => Ok((i3, h)),
1,235✔
321
        _ => Err(Err::Error(make_error(i3, ErrorKind::MapOpt))),
×
322
    }
323
}
1,235✔
324

325
fn http2_parse_headers_block_string(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
829✔
326
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
829✔
327
        bits(tuple((take_bits(1u8), take_bits(7u8))))(input)
829✔
328
    }
829✔
329
    let (i1, huffslen) = parser(input)?;
829✔
330
    let (i2, stringlen) = http2_parse_var_uint(i1, huffslen.1 as u64, 0x7F)?;
829✔
331
    let (i3, data) = take(stringlen as usize)(i2)?;
829✔
332
    if huffslen.0 == 0 {
829✔
333
        return Ok((i3, data.to_vec()));
79✔
334
    } else {
335
        let (_, val) = bits(many0(huffman::http2_decode_huffman))(data)?;
750✔
336
        return Ok((i3, val));
750✔
337
    }
338
}
829✔
339

340
fn http2_parse_headers_block_literal_common<'a>(
726✔
341
    input: &'a [u8], index: u64, dyn_headers: &HTTP2DynTable,
726✔
342
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
726✔
343
    let (i3, name, error) = if index == 0 {
726✔
344
        match http2_parse_headers_block_string(input) {
101✔
345
            Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)),
101✔
UNCOV
346
            Err(e) => Err(e),
×
347
        }
348
    } else {
349
        match http2_frame_header_static(index, dyn_headers) {
625✔
350
            Some(x) => Ok((
625✔
351
                input,
625✔
352
                x.name,
625✔
353
                HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
625✔
354
            )),
625✔
355
            None => Ok((
×
356
                input,
×
357
                Rc::new(Vec::new()),
×
358
                HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed,
×
359
            )),
×
360
        }
UNCOV
361
    }?;
×
362
    let (i4, value) = http2_parse_headers_block_string(i3)?;
726✔
363
    return Ok((
726✔
364
        i4,
726✔
365
        HTTP2FrameHeaderBlock {
726✔
366
            name,
726✔
367
            value: Rc::new(value),
726✔
368
            error,
726✔
369
            sizeupdate: 0,
726✔
370
        },
726✔
371
    ));
726✔
372
}
726✔
373

374
fn http2_parse_headers_block_literal_incindex<'a>(
567✔
375
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
567✔
376
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
567✔
377
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
567✔
378
        bits(complete(tuple((
567✔
379
            verify(take_bits(2u8), |&x| x == 1),
567✔
380
            take_bits(6u8),
567✔
381
        ))))(input)
567✔
382
    }
567✔
383
    let (i2, indexed) = parser(input)?;
567✔
384
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x3F)?;
567✔
385
    let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers);
567✔
386
    match r {
567✔
387
        Ok((r, head)) => {
567✔
388
            let headcopy = HTTP2FrameHeaderBlock {
567✔
389
                name: head.name.clone(),
567✔
390
                value: head.value.clone(),
567✔
391
                error: head.error,
567✔
392
                sizeupdate: 0,
567✔
393
            };
567✔
394
            if head.error == HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess {
567✔
395
                dyn_headers.current_size += 32 + headcopy.name.len() + headcopy.value.len();
567✔
396
                //in case of overflow, best effort is to keep first headers
567✔
397
                if dyn_headers.overflow > 0 {
567✔
UNCOV
398
                    if dyn_headers.overflow == 1 {
×
UNCOV
399
                        if dyn_headers.current_size <= (unsafe { HTTP2_MAX_TABLESIZE } as usize) {
×
UNCOV
400
                            //overflow had not yet happened
×
UNCOV
401
                            dyn_headers.table.push(headcopy);
×
UNCOV
402
                        } else if dyn_headers.current_size > dyn_headers.max_size {
×
403
                            //overflow happens, we cannot replace evicted headers
×
404
                            dyn_headers.overflow = 2;
×
405
                        }
×
406
                    }
×
407
                } else {
567✔
408
                    dyn_headers.table.push(headcopy);
567✔
409
                }
567✔
410
                let mut toremove = 0;
567✔
411
                while dyn_headers.current_size > dyn_headers.max_size
578✔
412
                    && toremove < dyn_headers.table.len()
11✔
413
                {
11✔
414
                    dyn_headers.current_size -= 32
11✔
415
                        + dyn_headers.table[toremove].name.len()
11✔
416
                        + dyn_headers.table[toremove].value.len();
11✔
417
                    toremove += 1;
11✔
418
                }
11✔
419
                dyn_headers.table.drain(0..toremove);
567✔
420
            }
×
421
            return Ok((r, head));
567✔
422
        }
UNCOV
423
        Err(e) => {
×
UNCOV
424
            return Err(e);
×
425
        }
426
    }
427
}
567✔
428

429
fn http2_parse_headers_block_literal_noindex<'a>(
159✔
430
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
159✔
431
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
159✔
432
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
159✔
433
        bits(complete(tuple((
159✔
434
            verify(take_bits(4u8), |&x| x == 0),
159✔
435
            take_bits(4u8),
159✔
436
        ))))(input)
159✔
437
    }
159✔
438
    let (i2, indexed) = parser(input)?;
159✔
439
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?;
159✔
440
    let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers);
159✔
441
    return r;
159✔
442
}
159✔
443

UNCOV
444
fn http2_parse_headers_block_literal_neverindex<'a>(
×
UNCOV
445
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
×
UNCOV
446
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
×
UNCOV
447
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
×
UNCOV
448
        bits(complete(tuple((
×
UNCOV
449
            verify(take_bits(4u8), |&x| x == 1),
×
UNCOV
450
            take_bits(4u8),
×
UNCOV
451
        ))))(input)
×
UNCOV
452
    }
×
UNCOV
453
    let (i2, indexed) = parser(input)?;
×
UNCOV
454
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?;
×
UNCOV
455
    let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers);
×
UNCOV
456
    return r;
×
UNCOV
457
}
×
458

459
fn http2_parse_var_uint(input: &[u8], value: u64, max: u64) -> IResult<&[u8], u64> {
2,813✔
460
    if value < max {
2,813✔
461
        return Ok((input, value));
2,652✔
462
    }
161✔
463
    let (i2, varia) = take_while(|ch| (ch & 0x80) != 0)(input)?;
178✔
464
    let (i3, finalv) = be_u8(i2)?;
161✔
465
    if varia.len() > 9 || (varia.len() == 9 && finalv > 1) {
161✔
466
        // this will overflow u64
UNCOV
467
        return Ok((i3, 0));
×
468
    }
161✔
469
    let mut varval = max;
161✔
470
    for (i, e) in varia.iter().enumerate() {
161✔
471
        varval += ((e & 0x7F) as u64) << (7 * i);
17✔
472
    }
17✔
473
    match varval.checked_add((finalv as u64) << (7 * varia.len())) {
161✔
474
        None => {
475
            return Err(Err::Error(make_error(i3, ErrorKind::LengthValue)));
×
476
        }
477
        Some(x) => {
161✔
478
            return Ok((i3, x));
161✔
479
        }
480
    }
481
}
2,813✔
482

483
fn http2_parse_headers_block_dynamic_size<'a>(
23✔
484
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
23✔
485
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
23✔
486
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
23✔
487
        bits(complete(tuple((
23✔
488
            verify(take_bits(3u8), |&x| x == 1),
23✔
489
            take_bits(5u8),
23✔
490
        ))))(input)
23✔
491
    }
23✔
492
    let (i2, maxsize) = parser(input)?;
23✔
493
    let (i3, maxsize2) = http2_parse_var_uint(i2, maxsize.1 as u64, 0x1F)?;
23✔
494
    if (maxsize2 as usize) < dyn_headers.max_size {
23✔
495
        //dyn_headers.max_size is updated later with all headers
496
        //may evict entries
497
        let mut toremove = 0;
17✔
498
        while dyn_headers.current_size > (maxsize2 as usize) && toremove < dyn_headers.table.len() {
41✔
499
            // we check dyn_headers.table as we may be in best effort
24✔
500
            // because the previous maxsize was too big for us to retain all the headers
24✔
501
            dyn_headers.current_size -= 32
24✔
502
                + dyn_headers.table[toremove].name.len()
24✔
503
                + dyn_headers.table[toremove].value.len();
24✔
504
            toremove += 1;
24✔
505
        }
24✔
506
        dyn_headers.table.drain(0..toremove);
17✔
507
    }
6✔
508
    return Ok((
23✔
509
        i3,
23✔
510
        HTTP2FrameHeaderBlock {
23✔
511
            name: Rc::new(Vec::new()),
23✔
512
            value: Rc::new(Vec::new()),
23✔
513
            error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate,
23✔
514
            sizeupdate: maxsize2,
23✔
515
        },
23✔
516
    ));
23✔
517
}
23✔
518

519
fn http2_parse_headers_block<'a>(
1,984✔
520
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
1,984✔
521
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,984✔
522
    //caller guarantees o have at least one byte
1,984✔
523
    if input[0] & 0x80 != 0 {
1,984✔
524
        return http2_parse_headers_block_indexed(input, dyn_headers);
1,235✔
525
    } else if input[0] & 0x40 != 0 {
749✔
526
        return http2_parse_headers_block_literal_incindex(input, dyn_headers);
567✔
527
    } else if input[0] & 0x20 != 0 {
182✔
528
        return http2_parse_headers_block_dynamic_size(input, dyn_headers);
23✔
529
    } else if input[0] & 0x10 != 0 {
159✔
UNCOV
530
        return http2_parse_headers_block_literal_neverindex(input, dyn_headers);
×
531
    } else {
532
        return http2_parse_headers_block_literal_noindex(input, dyn_headers);
159✔
533
    }
534
}
1,984✔
535

536
#[derive(Clone, Debug)]
537
pub struct HTTP2FrameHeaders {
538
    pub padlength: Option<u8>,
539
    pub priority: Option<HTTP2FrameHeadersPriority>,
540
    pub blocks: Vec<HTTP2FrameHeaderBlock>,
541
}
542

543
//end stream
544
pub const HTTP2_FLAG_HEADER_EOS: u8 = 0x1;
545
pub const HTTP2_FLAG_HEADER_END_HEADERS: u8 = 0x4;
546
pub const HTTP2_FLAG_HEADER_PADDED: u8 = 0x8;
547
const HTTP2_FLAG_HEADER_PRIORITY: u8 = 0x20;
548

549
fn http2_parse_headers_blocks<'a>(
209✔
550
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
209✔
551
) -> IResult<&'a [u8], Vec<HTTP2FrameHeaderBlock>> {
209✔
552
    let mut blocks = Vec::new();
209✔
553
    let mut i3 = input;
209✔
554
    while !i3.is_empty() {
2,187✔
555
        match http2_parse_headers_block(i3, dyn_headers) {
1,978✔
556
            Ok((rem, b)) => {
1,978✔
557
                blocks.push(b);
1,978✔
558
                debug_validate_bug_on!(i3.len() == rem.len());
1,978✔
559
                if i3.len() == rem.len() {
1,978✔
560
                    //infinite loop
561
                    return Err(Err::Error(make_error(input, ErrorKind::Eof)));
×
562
                }
1,978✔
563
                i3 = rem;
1,978✔
564
            }
565
            Err(Err::Error(ref err)) => {
×
566
                // if we error from http2_parse_var_uint, we keep the first parsed headers
×
567
                if err.code == ErrorKind::LengthValue {
×
568
                    blocks.push(HTTP2FrameHeaderBlock {
×
569
                        name: Rc::new(Vec::new()),
×
570
                        value: Rc::new(Vec::new()),
×
571
                        error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow,
×
572
                        sizeupdate: 0,
×
573
                    });
×
574
                    break;
×
575
                }
×
576
            }
UNCOV
577
            Err(x) => {
×
UNCOV
578
                return Err(x);
×
579
            }
580
        }
581
    }
582
    return Ok((i3, blocks));
209✔
583
}
209✔
584

585
pub fn http2_parse_frame_headers<'a>(
208✔
586
    input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
208✔
587
) -> IResult<&'a [u8], HTTP2FrameHeaders> {
208✔
588
    let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?;
208✔
589
    let (i3, priority) = cond(
208✔
590
        flags & HTTP2_FLAG_HEADER_PRIORITY != 0,
208✔
591
        http2_parse_headers_priority,
208✔
592
    )(i2)?;
208✔
593
    let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?;
208✔
594
    return Ok((
208✔
595
        i3,
208✔
596
        HTTP2FrameHeaders {
208✔
597
            padlength,
208✔
598
            priority,
208✔
599
            blocks,
208✔
600
        },
208✔
601
    ));
208✔
602
}
208✔
603

604
#[derive(Clone, Debug)]
605
pub struct HTTP2FramePushPromise {
606
    pub padlength: Option<u8>,
607
    pub reserved: u8,
608
    pub stream_id: u32,
609
    pub blocks: Vec<HTTP2FrameHeaderBlock>,
610
}
611

UNCOV
612
pub fn http2_parse_frame_push_promise<'a>(
×
UNCOV
613
    input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
×
UNCOV
614
) -> IResult<&'a [u8], HTTP2FramePushPromise> {
×
UNCOV
615
    let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?;
×
UNCOV
616
    let (i3, stream_id) = bits(tuple((take_bits(1u8), take_bits(31u32))))(i2)?;
×
UNCOV
617
    let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?;
×
UNCOV
618
    return Ok((
×
UNCOV
619
        i3,
×
UNCOV
620
        HTTP2FramePushPromise {
×
UNCOV
621
            padlength,
×
UNCOV
622
            reserved: stream_id.0,
×
UNCOV
623
            stream_id: stream_id.1,
×
UNCOV
624
            blocks,
×
UNCOV
625
        },
×
UNCOV
626
    ));
×
UNCOV
627
}
×
628

629
#[derive(Clone, Debug)]
630
pub struct HTTP2FrameContinuation {
631
    pub blocks: Vec<HTTP2FrameHeaderBlock>,
632
}
633

634
pub fn http2_parse_frame_continuation<'a>(
1✔
635
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
1✔
636
) -> IResult<&'a [u8], HTTP2FrameContinuation> {
1✔
637
    let (i3, blocks) = http2_parse_headers_blocks(input, dyn_headers)?;
1✔
638
    return Ok((i3, HTTP2FrameContinuation { blocks }));
1✔
639
}
1✔
640

641
#[repr(u16)]
642
#[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
253✔
643
pub enum HTTP2SettingsId {
644
    HeaderTableSize = 1,
645
    EnablePush = 2,
646
    MaxConcurrentStreams = 3,
647
    InitialWindowSize = 4,
648
    MaxFrameSize = 5,
649
    MaxHeaderListSize = 6,
650
    EnableConnectProtocol = 8, // rfc8441
651
    NoRfc7540Priorities = 9, // rfc9218
652
}
653

654
impl fmt::Display for HTTP2SettingsId {
655
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283✔
656
        write!(f, "{:?}", self)
283✔
657
    }
283✔
658
}
659

660
impl std::str::FromStr for HTTP2SettingsId {
661
    type Err = String;
662

663
    fn from_str(s: &str) -> Result<Self, Self::Err> {
10✔
664
        let su = s.to_uppercase();
10✔
665
        let su_slice: &str = &su;
10✔
666
        match su_slice {
10✔
667
            "SETTINGS_HEADER_TABLE_SIZE" => Ok(HTTP2SettingsId::HeaderTableSize),
10✔
668
            "SETTINGS_ENABLE_PUSH" => Ok(HTTP2SettingsId::EnablePush),
7✔
669
            "SETTINGS_MAX_CONCURRENT_STREAMS" => Ok(HTTP2SettingsId::MaxConcurrentStreams),
5✔
670
            "SETTINGS_INITIAL_WINDOW_SIZE" => Ok(HTTP2SettingsId::InitialWindowSize),
1✔
671
            "SETTINGS_MAX_FRAME_SIZE" => Ok(HTTP2SettingsId::MaxFrameSize),
1✔
672
            "SETTINGS_MAX_HEADER_LIST_SIZE" => Ok(HTTP2SettingsId::MaxHeaderListSize),
1✔
UNCOV
673
            "SETTINGS_ENABLE_CONNECT_PROTOCOL" => Ok(HTTP2SettingsId::EnableConnectProtocol),
×
UNCOV
674
            "SETTINGS_NO_RFC7540_PRIORITIES" => Ok(HTTP2SettingsId::NoRfc7540Priorities),
×
UNCOV
675
            _ => Err(format!("'{}' is not a valid value for HTTP2SettingsId", s)),
×
676
        }
677
    }
10✔
678
}
679

680
pub struct DetectHTTP2settingsSigCtx {
681
    pub id: HTTP2SettingsId,                //identifier
682
    pub value: Option<DetectUintData<u32>>, //optional value
683
}
684

685
pub fn http2_parse_settingsctx(i: &str) -> nom8::IResult<&str, DetectHTTP2settingsSigCtx> {
10✔
686
    use nom8::Parser;
687
    use nom8::bytes::complete::{is_a as is_a8, is_not as is_not8};
688
    use nom8::combinator::{complete as complete8, map_opt as map_opt8, opt as opt8, rest as rest8};
689
    use nom8::branch::alt as alt8;
690

691
    let (i, _) = opt8(is_a8(" ")).parse(i)?;
10✔
692
    let (i, id) = map_opt8(alt8((complete8(is_not8(" <>=")), rest8)), |s: &str| {
10✔
693
        HTTP2SettingsId::from_str(s).ok()
10✔
694
    }).parse(i)?;
10✔
695
    let (i, value) = opt8(complete8(detect_parse_uint)).parse(i)?;
10✔
696
    Ok((i, DetectHTTP2settingsSigCtx { id, value }))
10✔
697
}
10✔
698

699
#[derive(Clone, Copy, Debug)]
700
pub struct HTTP2FrameSettings {
701
    pub id: HTTP2SettingsId,
702
    pub value: u32,
703
}
704

705
fn http2_parse_frame_setting(i: &[u8]) -> IResult<&[u8], HTTP2FrameSettings> {
338✔
706
    let (i, id) = map_opt(be_u16, num::FromPrimitive::from_u16)(i)?;
338✔
707
    let (i, value) = be_u32(i)?;
253✔
708
    Ok((i, HTTP2FrameSettings { id, value }))
253✔
709
}
338✔
710

711
pub fn http2_parse_frame_settings(i: &[u8]) -> IResult<&[u8], Vec<HTTP2FrameSettings>> {
85✔
712
    many0(complete(http2_parse_frame_setting))(i)
85✔
713
}
85✔
714

715
pub fn doh_extract_request(i: &[u8]) -> IResult<&[u8], Vec<u8>> {
38✔
716
    let (i, _) = tag("/dns-query?dns=")(i)?;
38✔
717
    match STANDARD_NO_PAD.decode(i) {
4✔
718
        Ok(dec) => {
4✔
719
            // i is unused
4✔
720
            return Ok((i, dec));
4✔
721
        }
722
        _ => {
UNCOV
723
            return Err(Err::Error(make_error(i, ErrorKind::MapOpt)));
×
724
        }
725
    }
726
}
38✔
727

728
#[cfg(test)]
729
mod tests {
730

731
    use super::*;
732
    use crate::detect::uint::DetectUintMode;
733

734
    #[test]
735
    fn test_http2_parse_header() {
1✔
736
        let buf0: &[u8] = &[0x82];
1✔
737
        let mut dynh = HTTP2DynTable::new();
1✔
738
        let r0 = http2_parse_headers_block(buf0, &mut dynh);
1✔
739
        match r0 {
740
            Ok((remainder, hd)) => {
1✔
741
                // Check the first message.
1✔
742
                assert_eq!(hd.name, ":method".as_bytes().to_vec().into());
1✔
743
                assert_eq!(hd.value, "GET".as_bytes().to_vec().into());
1✔
744
                // And we should have no bytes left.
745
                assert_eq!(remainder.len(), 0);
1✔
746
            }
747
            Err(Err::Incomplete(_)) => {
748
                panic!("Result should not have been incomplete.");
749
            }
750
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
751
                panic!("Result should not be an error: {:?}.", err);
752
            }
753
        }
754
        let buf1: &[u8] = &[0x53, 0x03, 0x2A, 0x2F, 0x2A];
1✔
755
        let r1 = http2_parse_headers_block(buf1, &mut dynh);
1✔
756
        match r1 {
757
            Ok((remainder, hd)) => {
1✔
758
                // Check the first message.
1✔
759
                assert_eq!(hd.name, "accept".as_bytes().to_vec().into());
1✔
760
                assert_eq!(hd.value, "*/*".as_bytes().to_vec().into());
1✔
761
                // And we should have no bytes left.
762
                assert_eq!(remainder.len(), 0);
1✔
763
                assert_eq!(dynh.table.len(), 1);
1✔
764
            }
765
            Err(Err::Incomplete(_)) => {
766
                panic!("Result should not have been incomplete.");
767
            }
768
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
769
                panic!("Result should not be an error: {:?}.", err);
770
            }
771
        }
772
        let buf: &[u8] = &[
1✔
773
            0x41, 0x8a, 0xa0, 0xe4, 0x1d, 0x13, 0x9d, 0x09, 0xb8, 0xc8, 0x00, 0x0f,
1✔
774
        ];
1✔
775
        let result = http2_parse_headers_block(buf, &mut dynh);
1✔
776
        match result {
777
            Ok((remainder, hd)) => {
1✔
778
                // Check the first message.
1✔
779
                assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
1✔
780
                assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
1✔
781
                // And we should have no bytes left.
782
                assert_eq!(remainder.len(), 0);
1✔
783
                assert_eq!(dynh.table.len(), 2);
1✔
784
            }
785
            Err(Err::Incomplete(_)) => {
786
                panic!("Result should not have been incomplete.");
787
            }
788
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
789
                panic!("Result should not be an error: {:?}.", err);
790
            }
791
        }
792
        let buf3: &[u8] = &[0xbe];
1✔
793
        let r3 = http2_parse_headers_block(buf3, &mut dynh);
1✔
794
        match r3 {
795
            Ok((remainder, hd)) => {
1✔
796
                // same as before
1✔
797
                assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
1✔
798
                assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
1✔
799
                // And we should have no bytes left.
800
                assert_eq!(remainder.len(), 0);
1✔
801
                assert_eq!(dynh.table.len(), 2);
1✔
802
            }
803
            Err(Err::Incomplete(_)) => {
804
                panic!("Result should not have been incomplete.");
805
            }
806
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
807
                panic!("Result should not be an error: {:?}.", err);
808
            }
809
        }
810
        let buf4: &[u8] = &[0x80];
1✔
811
        let r4 = http2_parse_headers_block(buf4, &mut dynh);
1✔
812
        match r4 {
813
            Ok((remainder, hd)) => {
1✔
814
                assert_eq!(hd.error, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0);
1✔
815
                assert_eq!(remainder.len(), 0);
1✔
816
                assert_eq!(dynh.table.len(), 2);
1✔
817
            }
818
            Err(Err::Incomplete(_)) => {
819
                panic!("Result should not have been incomplete.");
820
            }
821
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
822
                panic!("Result should not be an error: {:?}.", err);
823
            }
824
        }
825
        let buf2: &[u8] = &[
1✔
826
            0x04, 0x94, 0x62, 0x43, 0x91, 0x8a, 0x47, 0x55, 0xa3, 0xa1, 0x89, 0xd3, 0x4d, 0x0c,
1✔
827
            0x1a, 0xa9, 0x0b, 0xe5, 0x79, 0xd3, 0x4d, 0x1f,
1✔
828
        ];
1✔
829
        let r2 = http2_parse_headers_block(buf2, &mut dynh);
1✔
830
        match r2 {
831
            Ok((remainder, hd)) => {
1✔
832
                // Check the first message.
1✔
833
                assert_eq!(hd.name, ":path".as_bytes().to_vec().into());
1✔
834
                assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into());
1✔
835
                // And we should have no bytes left.
836
                assert_eq!(remainder.len(), 0);
1✔
837
                assert_eq!(dynh.table.len(), 2);
1✔
838
            }
839
            Err(Err::Incomplete(_)) => {
840
                panic!("Result should not have been incomplete.");
841
            }
842
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
843
                panic!("Result should not be an error: {:?}.", err);
844
            }
845
        }
846
    }
1✔
847

848
    /// Simple test of some valid data.
849
    #[test]
850
    fn test_http2_parse_settingsctx() {
1✔
851
        let s = "SETTINGS_ENABLE_PUSH";
1✔
852
        let r = http2_parse_settingsctx(s);
1✔
853
        match r {
1✔
854
            Ok((rem, ctx)) => {
1✔
855
                assert_eq!(ctx.id, HTTP2SettingsId::EnablePush);
1✔
856
                assert!(ctx.value.is_none());
1✔
857
                assert_eq!(rem.len(), 0);
1✔
858
            }
859
            Err(e) => {
860
                panic!("Result should not be an error {:?}.", e);
861
            }
862
        }
863

864
        //spaces in the end
865
        let s1 = "SETTINGS_ENABLE_PUSH ";
1✔
866
        let r1 = http2_parse_settingsctx(s1);
1✔
867
        match r1 {
1✔
868
            Ok((rem, ctx)) => {
1✔
869
                assert_eq!(ctx.id, HTTP2SettingsId::EnablePush);
1✔
870
                if ctx.value.is_some() {
1✔
871
                    panic!("Unexpected value");
872
                }
1✔
873
                assert_eq!(rem.len(), 1);
1✔
874
            }
875
            Err(e) => {
876
                panic!("Result should not be an error {:?}.", e);
877
            }
878
        }
879

880
        let s2 = "SETTINGS_MAX_CONCURRENT_STREAMS  42";
1✔
881
        let r2 = http2_parse_settingsctx(s2);
1✔
882
        match r2 {
1✔
883
            Ok((rem, ctx)) => {
1✔
884
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
1✔
885
                match ctx.value {
1✔
886
                    Some(ctxval) => {
1✔
887
                        assert_eq!(ctxval.arg1, 42);
1✔
888
                    }
889
                    None => {
890
                        panic!("No value");
891
                    }
892
                }
893
                assert_eq!(rem.len(), 0);
1✔
894
            }
895
            Err(e) => {
896
                panic!("Result should not be an error {:?}.", e);
897
            }
898
        }
899

900
        let s3 = "SETTINGS_MAX_CONCURRENT_STREAMS 42-68";
1✔
901
        let r3 = http2_parse_settingsctx(s3);
1✔
902
        match r3 {
1✔
903
            Ok((rem, ctx)) => {
1✔
904
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
1✔
905
                match ctx.value {
1✔
906
                    Some(ctxval) => {
1✔
907
                        assert_eq!(ctxval.arg1, 42);
1✔
908
                        assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeRange);
1✔
909
                        assert_eq!(ctxval.arg2, 68);
1✔
910
                    }
911
                    None => {
912
                        panic!("No value");
913
                    }
914
                }
915
                assert_eq!(rem.len(), 0);
1✔
916
            }
917
            Err(e) => {
918
                panic!("Result should not be an error {:?}.", e);
919
            }
920
        }
921

922
        let s4 = "SETTINGS_MAX_CONCURRENT_STREAMS<54";
1✔
923
        let r4 = http2_parse_settingsctx(s4);
1✔
924
        match r4 {
1✔
925
            Ok((rem, ctx)) => {
1✔
926
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
1✔
927
                match ctx.value {
1✔
928
                    Some(ctxval) => {
1✔
929
                        assert_eq!(ctxval.arg1, 54);
1✔
930
                        assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeLt);
1✔
931
                    }
932
                    None => {
933
                        panic!("No value");
934
                    }
935
                }
936
                assert_eq!(rem.len(), 0);
1✔
937
            }
938
            Err(e) => {
939
                panic!("Result should not be an error {:?}.", e);
940
            }
941
        }
942

943
        let s5 = "SETTINGS_MAX_CONCURRENT_STREAMS > 76";
1✔
944
        let r5 = http2_parse_settingsctx(s5);
1✔
945
        match r5 {
1✔
946
            Ok((rem, ctx)) => {
1✔
947
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
1✔
948
                match ctx.value {
1✔
949
                    Some(ctxval) => {
1✔
950
                        assert_eq!(ctxval.arg1, 76);
1✔
951
                        assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeGt);
1✔
952
                    }
953
                    None => {
954
                        panic!("No value");
955
                    }
956
                }
957
                assert_eq!(rem.len(), 0);
1✔
958
            }
959
            Err(e) => {
960
                panic!("Result should not be an error {:?}.", e);
961
            }
962
        }
963
    }
1✔
964

965
    #[test]
966
    fn test_http2_parse_headers_block_string() {
1✔
967
        let buf: &[u8] = &[0x01, 0xFF];
1✔
968
        let r = http2_parse_headers_block_string(buf);
1✔
969
        match r {
970
            Ok((remainder, _)) => {
1✔
971
                assert_eq!(remainder.len(), 0);
1✔
972
            }
973
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
974
                panic!("Result should not be an error: {:?}.", err);
975
            }
976
            _ => {
977
                panic!("Result should have been ok");
978
            }
979
        }
980
        let buf2: &[u8] = &[0x83, 0xFF, 0xFF, 0xEA];
1✔
981
        let r2 = http2_parse_headers_block_string(buf2);
1✔
982
        match r2 {
1✔
983
            Ok((remainder, _)) => {
1✔
984
                assert_eq!(remainder.len(), 0);
1✔
985
            }
986
            _ => {
987
                panic!("Result should have been ok");
988
            }
989
        }
990
    }
1✔
991

992
    #[test]
993
    fn test_http2_parse_frame_header() {
1✔
994
        let buf: &[u8] = &[
1✔
995
            0x00, 0x00, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
1✔
996
            0x64,
1✔
997
        ];
1✔
998
        let result = http2_parse_frame_header(buf);
1✔
999
        match result {
1000
            Ok((remainder, frame)) => {
1✔
1001
                // Check the first message.
1✔
1002
                assert_eq!(frame.length, 6);
1✔
1003
                assert_eq!(frame.ftype, HTTP2FrameType::Settings as u8);
1✔
1004
                assert_eq!(frame.flags, 0);
1✔
1005
                assert_eq!(frame.reserved, 0);
1✔
1006
                assert_eq!(frame.stream_id, 0);
1✔
1007

1008
                // And we should have 6 bytes left.
1009
                assert_eq!(remainder.len(), 6);
1✔
1010
            }
1011
            Err(Err::Incomplete(_)) => {
1012
                panic!("Result should not have been incomplete.");
1013
            }
1014
            Err(Err::Error(err)) | Err(Err::Failure(err)) => {
1015
                panic!("Result should not be an error: {:?}.", err);
1016
            }
1017
        }
1018
    }
1✔
1019
}
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