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

OISF / suricata / 23338889526

20 Mar 2026 10:29AM UTC coverage: 76.331% (-3.0%) from 79.315%
23338889526

Pull #15053

github

web-flow
Merge 00ac1dd14 into 6587e363a
Pull Request #15053: Flow queue/v3

106 of 127 new or added lines in 8 files covered. (83.46%)

9913 existing lines in 468 files now uncovered.

255689 of 334972 relevant lines covered (76.33%)

4170649.82 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,904✔
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> {
2,129✔
65
    let (i, length) = be_u24(i)?;
2,129✔
66
    let (i, ftype) = be_u8(i)?;
2,129✔
67
    let (i, flags) = be_u8(i)?;
2,129✔
68
    let (i, b) = be_u32(i)?;
2,129✔
69
    let (reserved, stream_id) = ((b >> 31) as u8, b & 0x7fff_ffff);
2,129✔
70
    Ok((
2,129✔
71
        i,
2,129✔
72
        HTTP2FrameHeader {
2,129✔
73
            length,
2,129✔
74
            ftype,
2,129✔
75
            flags,
2,129✔
76
            reserved,
2,129✔
77
            stream_id,
2,129✔
78
        },
2,129✔
79
    ))
2,129✔
80
}
2,129✔
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> {
17✔
109
    let (i, _last_stream_id) = be_u32(i)?;
17✔
110
    let (i, errorcode) = be_u32(i)?;
17✔
111
    Ok((i, HTTP2FrameGoAway { errorcode }))
17✔
112
}
17✔
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> {
36✔
120
    let (i, errorcode) = be_u32(i)?;
36✔
121
    Ok((i, HTTP2FrameRstStream { errorcode }))
36✔
122
}
36✔
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> {
12✔
132
    let (i, b) = be_u32(i)?;
12✔
133
    let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff);
12✔
134
    let (i, weight) = be_u8(i)?;
12✔
135
    Ok((
12✔
136
        i,
12✔
137
        HTTP2FramePriority {
12✔
138
            exclusive,
12✔
139
            dependency,
12✔
140
            weight,
12✔
141
        },
12✔
142
    ))
12✔
143
}
12✔
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> {
229✔
152
    let (i, b) = be_u32(i)?;
229✔
153
    let (reserved, sizeinc) = ((b >> 31) as u8, b & 0x7fff_ffff);
229✔
154
    Ok((i, HTTP2FrameWindowUpdate { reserved, sizeinc }))
229✔
155
}
229✔
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> {
129✔
165
    let (i, b) = be_u32(i)?;
129✔
166
    let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff);
129✔
167
    let (i, weight) = be_u8(i)?;
129✔
168
    Ok((
129✔
169
        i,
129✔
170
        HTTP2FrameHeadersPriority {
129✔
171
            exclusive,
129✔
172
            dependency,
129✔
173
            weight,
129✔
174
        },
129✔
175
    ))
129✔
176
}
129✔
177

178
pub const HTTP2_STATIC_HEADERS_NUMBER: usize = 61;
179

180
fn http2_frame_header_static(n: u64, dyn_headers: &HTTP2DynTable) -> Option<HTTP2FrameHeaderBlock> {
3,143✔
181
    let (name, value) = match n {
3,143✔
182
        1 => (":authority", ""),
38✔
183
        2 => (":method", "GET"),
73✔
184
        3 => (":method", "POST"),
84✔
185
        4 => (":path", "/"),
86✔
186
        5 => (":path", "/index.html"),
71✔
187
        6 => (":scheme", "http"),
57✔
188
        7 => (":scheme", "https"),
99✔
189
        8 => (":status", "200"),
149✔
UNCOV
190
        9 => (":status", "204"),
×
191
        10 => (":status", "206"),
12✔
UNCOV
192
        11 => (":status", "304"),
×
193
        12 => (":status", "400"),
2✔
194
        13 => (":status", "404"),
8✔
195
        14 => (":status", "500"),
4✔
UNCOV
196
        15 => ("accept-charset", ""),
×
197
        16 => ("accept-encoding", "gzip, deflate"),
61✔
198
        17 => ("accept-language", ""),
12✔
199
        18 => ("accept-ranges", ""),
19✔
200
        19 => ("accept", ""),
35✔
201
        20 => ("access-control-allow-origin", ""),
10✔
202
        21 => ("age", ""),
5✔
UNCOV
203
        22 => ("allow", ""),
×
UNCOV
204
        23 => ("authorization", ""),
×
205
        24 => ("cache-control", ""),
87✔
UNCOV
206
        25 => ("content-disposition", ""),
×
207
        26 => ("content-encoding", ""),
11✔
UNCOV
208
        27 => ("content-language", ""),
×
209
        28 => ("content-length", ""),
102✔
UNCOV
210
        29 => ("content-location", ""),
×
211
        30 => ("content-range", ""),
12✔
212
        31 => ("content-type", ""),
69✔
213
        32 => ("cookie", ""),
14✔
214
        33 => ("date", ""),
95✔
215
        34 => ("etag", ""),
5✔
UNCOV
216
        35 => ("expect", ""),
×
217
        36 => ("expires", ""),
12✔
UNCOV
218
        37 => ("from", ""),
×
219
        38 => ("host", ""),
4✔
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", ""),
56✔
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", ""),
10✔
UNCOV
233
        52 => ("refresh", ""),
×
UNCOV
234
        53 => ("retry-after", ""),
×
235
        54 => ("server", ""),
40✔
236
        55 => ("set-cookie", ""),
2✔
237
        56 => ("strict-transport-security", ""),
6✔
UNCOV
238
        57 => ("transfer-encoding", ""),
×
239
        58 => ("user-agent", ""),
35✔
240
        59 => ("vary", ""),
17✔
241
        60 => ("via", ""),
5✔
UNCOV
242
        61 => ("www-authenticate", ""),
×
243
        _ => ("", ""),
1,736✔
244
    };
245
    if !name.is_empty() {
3,143✔
246
        return Some(HTTP2FrameHeaderBlock {
1,407✔
247
            name: Rc::new(name.as_bytes().to_vec()),
1,407✔
248
            value: Rc::new(value.as_bytes().to_vec()),
1,407✔
249
            error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1,407✔
250
            sizeupdate: 0,
1,407✔
251
        });
1,407✔
252
    } else {
253
        //use dynamic table
254
        if n == 0 {
1,736✔
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 {
1,735✔
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);
1,735✔
270
            let headcopy = HTTP2FrameHeaderBlock {
1,735✔
271
                name: dyn_headers.table[indyn].name.clone(),
1,735✔
272
                value: dyn_headers.table[indyn].value.clone(),
1,735✔
273
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1,735✔
274
                sizeupdate: 0,
1,735✔
275
            };
1,735✔
276
            return Some(headcopy);
1,735✔
277
        }
278
    }
279
}
3,143✔
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>(
2,153✔
309
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
2,153✔
310
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
2,153✔
311
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
2,153✔
312
        bits(complete(tuple((
2,153✔
313
            verify(take_bits(1u8), |&x| x == 1),
2,153✔
314
            take_bits(7u8),
2,153✔
315
        ))))(input)
2,153✔
316
    }
2,153✔
317
    let (i2, indexed) = parser(input)?;
2,153✔
318
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x7F)?;
2,153✔
319
    match http2_frame_header_static(indexreal, dyn_headers) {
2,153✔
320
        Some(h) => Ok((i3, h)),
2,153✔
321
        _ => Err(Err::Error(make_error(i3, ErrorKind::MapOpt))),
×
322
    }
323
}
2,153✔
324

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

340
fn http2_parse_headers_block_literal_common<'a>(
1,165✔
341
    input: &'a [u8], index: u64, dyn_headers: &HTTP2DynTable,
1,165✔
342
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,165✔
343
    let (i3, name, error) = if index == 0 {
1,165✔
344
        match http2_parse_headers_block_string(input) {
175✔
345
            Ok((r, n)) => Ok((r, Rc::new(n), HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess)),
175✔
UNCOV
346
            Err(e) => Err(e),
×
347
        }
348
    } else {
349
        match http2_frame_header_static(index, dyn_headers) {
990✔
350
            Some(x) => Ok((
990✔
351
                input,
990✔
352
                x.name,
990✔
353
                HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
990✔
354
            )),
990✔
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)?;
1,165✔
363
    return Ok((
1,165✔
364
        i4,
1,165✔
365
        HTTP2FrameHeaderBlock {
1,165✔
366
            name,
1,165✔
367
            value: Rc::new(value),
1,165✔
368
            error,
1,165✔
369
            sizeupdate: 0,
1,165✔
370
        },
1,165✔
371
    ));
1,165✔
372
}
1,165✔
373

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

429
fn http2_parse_headers_block_literal_noindex<'a>(
237✔
430
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
237✔
431
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
237✔
432
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
237✔
433
        bits(complete(tuple((
237✔
434
            verify(take_bits(4u8), |&x| x == 0),
237✔
435
            take_bits(4u8),
237✔
436
        ))))(input)
237✔
437
    }
237✔
438
    let (i2, indexed) = parser(input)?;
237✔
439
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?;
237✔
440
    let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers);
237✔
441
    return r;
237✔
442
}
237✔
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> {
4,691✔
460
    if value < max {
4,691✔
461
        return Ok((input, value));
4,430✔
462
    }
261✔
463
    let (i2, varia) = take_while(|ch| (ch & 0x80) != 0)(input)?;
285✔
464
    let (i3, finalv) = be_u8(i2)?;
261✔
465
    if varia.len() > 9 || (varia.len() == 9 && finalv > 1) {
261✔
466
        // this will overflow u64
UNCOV
467
        return Ok((i3, 0));
×
468
    }
261✔
469
    let mut varval = max;
261✔
470
    for (i, e) in varia.iter().enumerate() {
261✔
471
        varval += ((e & 0x7F) as u64) << (7 * i);
24✔
472
    }
24✔
473
    match varval.checked_add((finalv as u64) << (7 * varia.len())) {
261✔
474
        None => {
475
            return Err(Err::Error(make_error(i3, ErrorKind::LengthValue)));
×
476
        }
477
        Some(x) => {
261✔
478
            return Ok((i3, x));
261✔
479
        }
480
    }
481
}
4,691✔
482

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

519
fn http2_parse_headers_block<'a>(
3,349✔
520
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
3,349✔
521
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
3,349✔
522
    //caller guarantees o have at least one byte
3,349✔
523
    if input[0] & 0x80 != 0 {
3,349✔
524
        return http2_parse_headers_block_indexed(input, dyn_headers);
2,153✔
525
    } else if input[0] & 0x40 != 0 {
1,196✔
526
        return http2_parse_headers_block_literal_incindex(input, dyn_headers);
928✔
527
    } else if input[0] & 0x20 != 0 {
268✔
528
        return http2_parse_headers_block_dynamic_size(input, dyn_headers);
31✔
529
    } else if input[0] & 0x10 != 0 {
237✔
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);
237✔
533
    }
534
}
3,349✔
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>(
335✔
550
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
335✔
551
) -> IResult<&'a [u8], Vec<HTTP2FrameHeaderBlock>> {
335✔
552
    let mut blocks = Vec::new();
335✔
553
    let mut i3 = input;
335✔
554
    while !i3.is_empty() {
3,678✔
555
        match http2_parse_headers_block(i3, dyn_headers) {
3,343✔
556
            Ok((rem, b)) => {
3,343✔
557
                blocks.push(b);
3,343✔
558
                debug_validate_bug_on!(i3.len() == rem.len());
3,343✔
559
                if i3.len() == rem.len() {
3,343✔
560
                    //infinite loop
561
                    return Err(Err::Error(make_error(input, ErrorKind::Eof)));
×
562
                }
3,343✔
563
                i3 = rem;
3,343✔
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));
335✔
583
}
335✔
584

585
pub fn http2_parse_frame_headers<'a>(
333✔
586
    input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
333✔
587
) -> IResult<&'a [u8], HTTP2FrameHeaders> {
333✔
588
    let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?;
333✔
589
    let (i3, priority) = cond(
333✔
590
        flags & HTTP2_FLAG_HEADER_PRIORITY != 0,
333✔
591
        http2_parse_headers_priority,
333✔
592
    )(i2)?;
333✔
593
    let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?;
333✔
594
    return Ok((
333✔
595
        i3,
333✔
596
        HTTP2FrameHeaders {
333✔
597
            padlength,
333✔
598
            priority,
333✔
599
            blocks,
333✔
600
        },
333✔
601
    ));
333✔
602
}
333✔
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>(
2✔
635
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
2✔
636
) -> IResult<&'a [u8], HTTP2FrameContinuation> {
2✔
637
    let (i3, blocks) = http2_parse_headers_blocks(input, dyn_headers)?;
2✔
638
    return Ok((i3, HTTP2FrameContinuation { blocks }));
2✔
639
}
2✔
640

641
#[repr(u16)]
642
#[derive(Clone, Copy, PartialEq, Eq, FromPrimitive, Debug)]
364✔
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 {
394✔
656
        write!(f, "{:?}", self)
394✔
657
    }
394✔
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> {
482✔
706
    let (i, id) = map_opt(be_u16, num::FromPrimitive::from_u16)(i)?;
482✔
707
    let (i, value) = be_u32(i)?;
364✔
708
    Ok((i, HTTP2FrameSettings { id, value }))
364✔
709
}
482✔
710

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

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