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

OISF / suricata / 23352517976

20 Mar 2026 04:32PM UTC coverage: 65.866% (-13.4%) from 79.315%
23352517976

Pull #15072

github

web-flow
Merge abcd1935f into 6587e363a
Pull Request #15072: Stack 8001 v16.3

41 of 70 new or added lines in 10 files covered. (58.57%)

18894 existing lines in 577 files now uncovered.

143735 of 218225 relevant lines covered (65.87%)

4342818.61 hits per line

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

75.0
/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,516✔
65
    let (i, length) = be_u24(i)?;
1,516✔
66
    let (i, ftype) = be_u8(i)?;
1,516✔
67
    let (i, flags) = be_u8(i)?;
1,516✔
68
    let (i, b) = be_u32(i)?;
1,516✔
69
    let (reserved, stream_id) = ((b >> 31) as u8, b & 0x7fff_ffff);
1,516✔
70
    Ok((
1,516✔
71
        i,
1,516✔
72
        HTTP2FrameHeader {
1,516✔
73
            length,
1,516✔
74
            ftype,
1,516✔
75
            flags,
1,516✔
76
            reserved,
1,516✔
77
            stream_id,
1,516✔
78
        },
1,516✔
79
    ))
1,516✔
80
}
1,516✔
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,854✔
181
    let (name, value) = match n {
1,854✔
182
        1 => (":authority", ""),
24✔
183
        2 => (":method", "GET"),
54✔
184
        3 => (":method", "POST"),
42✔
185
        4 => (":path", "/"),
60✔
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", ""),
22✔
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
        _ => ("", ""),
952✔
244
    };
245
    if !name.is_empty() {
1,854✔
246
        return Some(HTTP2FrameHeaderBlock {
902✔
247
            name: Rc::new(name.as_bytes().to_vec()),
902✔
248
            value: Rc::new(value.as_bytes().to_vec()),
902✔
249
            error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
902✔
250
            sizeupdate: 0,
902✔
251
        });
902✔
252
    } else {
253
        //use dynamic table
254
        if n == 0 {
952✔
UNCOV
255
            return Some(HTTP2FrameHeaderBlock {
×
UNCOV
256
                name: Rc::new(Vec::new()),
×
UNCOV
257
                value: Rc::new(Vec::new()),
×
UNCOV
258
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0,
×
UNCOV
259
                sizeupdate: 0,
×
UNCOV
260
            });
×
261
        } else if dyn_headers.table.len() + HTTP2_STATIC_HEADERS_NUMBER < n as usize {
952✔
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);
952✔
270
            let headcopy = HTTP2FrameHeaderBlock {
952✔
271
                name: dyn_headers.table[indyn].name.clone(),
952✔
272
                value: dyn_headers.table[indyn].value.clone(),
952✔
273
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
952✔
274
                sizeupdate: 0,
952✔
275
            };
952✔
276
            return Some(headcopy);
952✔
277
        }
278
    }
279
}
1,854✔
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,232✔
309
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
1,232✔
310
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,232✔
311
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
1,232✔
312
        bits(complete(tuple((
1,232✔
313
            verify(take_bits(1u8), |&x| x == 1),
1,232✔
314
            take_bits(7u8),
1,232✔
315
        ))))(input)
1,232✔
316
    }
1,232✔
317
    let (i2, indexed) = parser(input)?;
1,232✔
318
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x7F)?;
1,232✔
319
    match http2_frame_header_static(indexreal, dyn_headers) {
1,232✔
320
        Some(h) => Ok((i3, h)),
1,232✔
321
        _ => Err(Err::Error(make_error(i3, ErrorKind::MapOpt))),
×
322
    }
323
}
1,232✔
324

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

340
fn http2_parse_headers_block_literal_common<'a>(
723✔
341
    input: &'a [u8], index: u64, dyn_headers: &HTTP2DynTable,
723✔
342
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
723✔
343
    let (i3, name, error) = if index == 0 {
723✔
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) {
622✔
350
            Some(x) => Ok((
622✔
351
                input,
622✔
352
                x.name,
622✔
353
                HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
622✔
354
            )),
622✔
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)?;
723✔
363
    return Ok((
723✔
364
        i4,
723✔
365
        HTTP2FrameHeaderBlock {
723✔
366
            name,
723✔
367
            value: Rc::new(value),
723✔
368
            error,
723✔
369
            sizeupdate: 0,
723✔
370
        },
723✔
371
    ));
723✔
372
}
723✔
373

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

429
fn http2_parse_headers_block_literal_noindex<'a>(
158✔
430
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
158✔
431
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
158✔
432
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
158✔
433
        bits(complete(tuple((
158✔
434
            verify(take_bits(4u8), |&x| x == 0),
158✔
435
            take_bits(4u8),
158✔
436
        ))))(input)
158✔
437
    }
158✔
438
    let (i2, indexed) = parser(input)?;
158✔
439
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0xF)?;
158✔
440
    let r = http2_parse_headers_block_literal_common(i3, indexreal, dyn_headers);
158✔
441
    return r;
158✔
442
}
158✔
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,802✔
460
    if value < max {
2,802✔
461
        return Ok((input, value));
2,641✔
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,802✔
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,978✔
520
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
1,978✔
521
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,978✔
522
    //caller guarantees o have at least one byte
1,978✔
523
    if input[0] & 0x80 != 0 {
1,978✔
524
        return http2_parse_headers_block_indexed(input, dyn_headers);
1,232✔
525
    } else if input[0] & 0x40 != 0 {
746✔
526
        return http2_parse_headers_block_literal_incindex(input, dyn_headers);
565✔
527
    } else if input[0] & 0x20 != 0 {
181✔
528
        return http2_parse_headers_block_dynamic_size(input, dyn_headers);
23✔
529
    } else if input[0] & 0x10 != 0 {
158✔
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);
158✔
533
    }
534
}
1,978✔
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> {
3✔
664
        let su = s.to_uppercase();
3✔
665
        let su_slice: &str = &su;
3✔
666
        match su_slice {
3✔
667
            "SETTINGS_HEADER_TABLE_SIZE" => Ok(HTTP2SettingsId::HeaderTableSize),
3✔
UNCOV
668
            "SETTINGS_ENABLE_PUSH" => Ok(HTTP2SettingsId::EnablePush),
×
UNCOV
669
            "SETTINGS_MAX_CONCURRENT_STREAMS" => Ok(HTTP2SettingsId::MaxConcurrentStreams),
×
UNCOV
670
            "SETTINGS_INITIAL_WINDOW_SIZE" => Ok(HTTP2SettingsId::InitialWindowSize),
×
UNCOV
671
            "SETTINGS_MAX_FRAME_SIZE" => Ok(HTTP2SettingsId::MaxFrameSize),
×
UNCOV
672
            "SETTINGS_MAX_HEADER_LIST_SIZE" => Ok(HTTP2SettingsId::MaxHeaderListSize),
×
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
    }
3✔
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> {
3✔
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)?;
3✔
692
    let (i, id) = map_opt8(alt8((complete8(is_not8(" <>=")), rest8)), |s: &str| {
3✔
693
        HTTP2SettingsId::from_str(s).ok()
3✔
694
    }).parse(i)?;
3✔
695
    let (i, value) = opt8(complete8(detect_parse_uint)).parse(i)?;
3✔
696
    Ok((i, DetectHTTP2settingsSigCtx { id, value }))
3✔
697
}
3✔
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() {
736
        let buf0: &[u8] = &[0x82];
737
        let mut dynh = HTTP2DynTable::new();
738
        let r0 = http2_parse_headers_block(buf0, &mut dynh);
739
        match r0 {
740
            Ok((remainder, hd)) => {
741
                // Check the first message.
742
                assert_eq!(hd.name, ":method".as_bytes().to_vec().into());
743
                assert_eq!(hd.value, "GET".as_bytes().to_vec().into());
744
                // And we should have no bytes left.
745
                assert_eq!(remainder.len(), 0);
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];
755
        let r1 = http2_parse_headers_block(buf1, &mut dynh);
756
        match r1 {
757
            Ok((remainder, hd)) => {
758
                // Check the first message.
759
                assert_eq!(hd.name, "accept".as_bytes().to_vec().into());
760
                assert_eq!(hd.value, "*/*".as_bytes().to_vec().into());
761
                // And we should have no bytes left.
762
                assert_eq!(remainder.len(), 0);
763
                assert_eq!(dynh.table.len(), 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] = &[
773
            0x41, 0x8a, 0xa0, 0xe4, 0x1d, 0x13, 0x9d, 0x09, 0xb8, 0xc8, 0x00, 0x0f,
774
        ];
775
        let result = http2_parse_headers_block(buf, &mut dynh);
776
        match result {
777
            Ok((remainder, hd)) => {
778
                // Check the first message.
779
                assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
780
                assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
781
                // And we should have no bytes left.
782
                assert_eq!(remainder.len(), 0);
783
                assert_eq!(dynh.table.len(), 2);
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];
793
        let r3 = http2_parse_headers_block(buf3, &mut dynh);
794
        match r3 {
795
            Ok((remainder, hd)) => {
796
                // same as before
797
                assert_eq!(hd.name, ":authority".as_bytes().to_vec().into());
798
                assert_eq!(hd.value, "localhost:3000".as_bytes().to_vec().into());
799
                // And we should have no bytes left.
800
                assert_eq!(remainder.len(), 0);
801
                assert_eq!(dynh.table.len(), 2);
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];
811
        let r4 = http2_parse_headers_block(buf4, &mut dynh);
812
        match r4 {
813
            Ok((remainder, hd)) => {
814
                assert_eq!(hd.error, HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIndex0);
815
                assert_eq!(remainder.len(), 0);
816
                assert_eq!(dynh.table.len(), 2);
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] = &[
826
            0x04, 0x94, 0x62, 0x43, 0x91, 0x8a, 0x47, 0x55, 0xa3, 0xa1, 0x89, 0xd3, 0x4d, 0x0c,
827
            0x1a, 0xa9, 0x0b, 0xe5, 0x79, 0xd3, 0x4d, 0x1f,
828
        ];
829
        let r2 = http2_parse_headers_block(buf2, &mut dynh);
830
        match r2 {
831
            Ok((remainder, hd)) => {
832
                // Check the first message.
833
                assert_eq!(hd.name, ":path".as_bytes().to_vec().into());
834
                assert_eq!(hd.value, "/doc/manual/html/index.html".as_bytes().to_vec().into());
835
                // And we should have no bytes left.
836
                assert_eq!(remainder.len(), 0);
837
                assert_eq!(dynh.table.len(), 2);
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
    }
847

848
    /// Simple test of some valid data.
849
    #[test]
850
    fn test_http2_parse_settingsctx() {
851
        let s = "SETTINGS_ENABLE_PUSH";
852
        let r = http2_parse_settingsctx(s);
853
        match r {
854
            Ok((rem, ctx)) => {
855
                assert_eq!(ctx.id, HTTP2SettingsId::EnablePush);
856
                assert!(ctx.value.is_none());
857
                assert_eq!(rem.len(), 0);
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 ";
866
        let r1 = http2_parse_settingsctx(s1);
867
        match r1 {
868
            Ok((rem, ctx)) => {
869
                assert_eq!(ctx.id, HTTP2SettingsId::EnablePush);
870
                if ctx.value.is_some() {
871
                    panic!("Unexpected value");
872
                }
873
                assert_eq!(rem.len(), 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";
881
        let r2 = http2_parse_settingsctx(s2);
882
        match r2 {
883
            Ok((rem, ctx)) => {
884
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
885
                match ctx.value {
886
                    Some(ctxval) => {
887
                        assert_eq!(ctxval.arg1, 42);
888
                    }
889
                    None => {
890
                        panic!("No value");
891
                    }
892
                }
893
                assert_eq!(rem.len(), 0);
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";
901
        let r3 = http2_parse_settingsctx(s3);
902
        match r3 {
903
            Ok((rem, ctx)) => {
904
                assert_eq!(ctx.id, HTTP2SettingsId::MaxConcurrentStreams);
905
                match ctx.value {
906
                    Some(ctxval) => {
907
                        assert_eq!(ctxval.arg1, 42);
908
                        assert_eq!(ctxval.mode, DetectUintMode::DetectUintModeRange);
909
                        assert_eq!(ctxval.arg2, 68);
910
                    }
911
                    None => {
912
                        panic!("No value");
913
                    }
914
                }
915
                assert_eq!(rem.len(), 0);
916
            }
917
            Err(e) => {
918
                panic!("Result should not be an error {:?}.", e);
919
            }
920
        }
921

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

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

965
    #[test]
966
    fn test_http2_parse_headers_block_string() {
967
        let buf: &[u8] = &[0x01, 0xFF];
968
        let r = http2_parse_headers_block_string(buf);
969
        match r {
970
            Ok((remainder, _)) => {
971
                assert_eq!(remainder.len(), 0);
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];
981
        let r2 = http2_parse_headers_block_string(buf2);
982
        match r2 {
983
            Ok((remainder, _)) => {
984
                assert_eq!(remainder.len(), 0);
985
            }
986
            _ => {
987
                panic!("Result should have been ok");
988
            }
989
        }
990
    }
991

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

1008
                // And we should have 6 bytes left.
1009
                assert_eq!(remainder.len(), 6);
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
    }
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