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

OISF / suricata / 22550902417

01 Mar 2026 07:32PM UTC coverage: 68.401% (-5.3%) from 73.687%
22550902417

Pull #14922

github

web-flow
github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14922: github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

218243 of 319063 relevant lines covered (68.4%)

3284926.58 hits per line

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

82.12
/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)]
528✔
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> {
613✔
65
    let (i, length) = be_u24(i)?;
613✔
66
    let (i, ftype) = be_u8(i)?;
613✔
67
    let (i, flags) = be_u8(i)?;
613✔
68
    let (i, b) = be_u32(i)?;
613✔
69
    let (reserved, stream_id) = ((b >> 31) as u8, b & 0x7fff_ffff);
613✔
70
    Ok((
613✔
71
        i,
613✔
72
        HTTP2FrameHeader {
613✔
73
            length,
613✔
74
            ftype,
613✔
75
            flags,
613✔
76
            reserved,
613✔
77
            stream_id,
613✔
78
        },
613✔
79
    ))
613✔
80
}
613✔
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> {
6✔
109
    let (i, _last_stream_id) = be_u32(i)?;
6✔
110
    let (i, errorcode) = be_u32(i)?;
6✔
111
    Ok((i, HTTP2FrameGoAway { errorcode }))
6✔
112
}
6✔
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> {
15✔
120
    let (i, errorcode) = be_u32(i)?;
15✔
121
    Ok((i, HTTP2FrameRstStream { errorcode }))
15✔
122
}
15✔
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> {
52✔
152
    let (i, b) = be_u32(i)?;
52✔
153
    let (reserved, sizeinc) = ((b >> 31) as u8, b & 0x7fff_ffff);
52✔
154
    Ok((i, HTTP2FrameWindowUpdate { reserved, sizeinc }))
52✔
155
}
52✔
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> {
52✔
165
    let (i, b) = be_u32(i)?;
52✔
166
    let (exclusive, dependency) = ((b >> 31) as u8, b & 0x7fff_ffff);
52✔
167
    let (i, weight) = be_u8(i)?;
52✔
168
    Ok((
52✔
169
        i,
52✔
170
        HTTP2FrameHeadersPriority {
52✔
171
            exclusive,
52✔
172
            dependency,
52✔
173
            weight,
52✔
174
        },
52✔
175
    ))
52✔
176
}
52✔
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,289✔
181
    let (name, value) = match n {
1,289✔
182
        1 => (":authority", ""),
14✔
183
        2 => (":method", "GET"),
19✔
184
        3 => (":method", "POST"),
42✔
185
        4 => (":path", "/"),
26✔
186
        5 => (":path", "/index.html"),
35✔
187
        6 => (":scheme", "http"),
11✔
188
        7 => (":scheme", "https"),
49✔
189
        8 => (":status", "200"),
57✔
190
        9 => (":status", "204"),
×
191
        10 => (":status", "206"),
3✔
192
        11 => (":status", "304"),
×
193
        12 => (":status", "400"),
1✔
194
        13 => (":status", "404"),
1✔
195
        14 => (":status", "500"),
2✔
196
        15 => ("accept-charset", ""),
×
197
        16 => ("accept-encoding", "gzip, deflate"),
15✔
198
        17 => ("accept-language", ""),
6✔
199
        18 => ("accept-ranges", ""),
4✔
200
        19 => ("accept", ""),
13✔
201
        20 => ("access-control-allow-origin", ""),
5✔
202
        21 => ("age", ""),
2✔
203
        22 => ("allow", ""),
×
204
        23 => ("authorization", ""),
×
205
        24 => ("cache-control", ""),
38✔
206
        25 => ("content-disposition", ""),
×
207
        26 => ("content-encoding", ""),
5✔
208
        27 => ("content-language", ""),
×
209
        28 => ("content-length", ""),
28✔
210
        29 => ("content-location", ""),
×
211
        30 => ("content-range", ""),
3✔
212
        31 => ("content-type", ""),
26✔
213
        32 => ("cookie", ""),
7✔
214
        33 => ("date", ""),
34✔
215
        34 => ("etag", ""),
×
216
        35 => ("expect", ""),
×
217
        36 => ("expires", ""),
6✔
218
        37 => ("from", ""),
×
219
        38 => ("host", ""),
2✔
220
        39 => ("if-match", ""),
×
221
        40 => ("if-modified-since", ""),
×
222
        41 => ("if-none-match", ""),
×
223
        42 => ("if-range", ""),
×
224
        43 => ("if-unmodified-since", ""),
×
225
        44 => ("last-modified", ""),
10✔
226
        45 => ("link", ""),
×
227
        46 => ("location", ""),
×
228
        47 => ("max-forwards", ""),
×
229
        48 => ("proxy-authenticate", ""),
×
230
        49 => ("proxy-authorization", ""),
×
231
        50 => ("range", ""),
×
232
        51 => ("referer", ""),
5✔
233
        52 => ("refresh", ""),
×
234
        53 => ("retry-after", ""),
×
235
        54 => ("server", ""),
12✔
236
        55 => ("set-cookie", ""),
1✔
237
        56 => ("strict-transport-security", ""),
3✔
238
        57 => ("transfer-encoding", ""),
×
239
        58 => ("user-agent", ""),
12✔
240
        59 => ("vary", ""),
8✔
241
        60 => ("via", ""),
×
242
        61 => ("www-authenticate", ""),
×
243
        _ => ("", ""),
784✔
244
    };
245
    if !name.is_empty() {
1,289✔
246
        return Some(HTTP2FrameHeaderBlock {
505✔
247
            name: Rc::new(name.as_bytes().to_vec()),
505✔
248
            value: Rc::new(value.as_bytes().to_vec()),
505✔
249
            error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
505✔
250
            sizeupdate: 0,
505✔
251
        });
505✔
252
    } else {
253
        //use dynamic table
254
        if n == 0 {
784✔
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 {
783✔
262
            return Some(HTTP2FrameHeaderBlock {
×
263
                name: Rc::new(Vec::new()),
×
264
                value: Rc::new(Vec::new()),
×
265
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeNotIndexed,
×
266
                sizeupdate: 0,
×
267
            });
×
268
        } else {
269
            let indyn = dyn_headers.table.len() - (n as usize - HTTP2_STATIC_HEADERS_NUMBER);
783✔
270
            let headcopy = HTTP2FrameHeaderBlock {
783✔
271
                name: dyn_headers.table[indyn].name.clone(),
783✔
272
                value: dyn_headers.table[indyn].value.clone(),
783✔
273
                error: HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
783✔
274
                sizeupdate: 0,
783✔
275
            };
783✔
276
            return Some(headcopy);
783✔
277
        }
278
    }
279
}
1,289✔
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 {
293
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
294
        write!(f, "{:?}", self)
×
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>(
921✔
309
    input: &'a [u8], dyn_headers: &HTTP2DynTable,
921✔
310
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
921✔
311
    fn parser(input: &[u8]) -> IResult<&[u8], (u8, u8)> {
921✔
312
        bits(complete(tuple((
921✔
313
            verify(take_bits(1u8), |&x| x == 1),
921✔
314
            take_bits(7u8),
921✔
315
        ))))(input)
921✔
316
    }
921✔
317
    let (i2, indexed) = parser(input)?;
921✔
318
    let (i3, indexreal) = http2_parse_var_uint(i2, indexed.1 as u64, 0x7F)?;
921✔
319
    match http2_frame_header_static(indexreal, dyn_headers) {
921✔
320
        Some(h) => Ok((i3, h)),
921✔
321
        _ => Err(Err::Error(make_error(i3, ErrorKind::MapOpt))),
×
322
    }
323
}
921✔
324

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

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

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

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

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

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

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

519
fn http2_parse_headers_block<'a>(
1,371✔
520
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
1,371✔
521
) -> IResult<&'a [u8], HTTP2FrameHeaderBlock> {
1,371✔
522
    //caller guarantees o have at least one byte
1,371✔
523
    if input[0] & 0x80 != 0 {
1,371✔
524
        return http2_parse_headers_block_indexed(input, dyn_headers);
921✔
525
    } else if input[0] & 0x40 != 0 {
450✔
526
        return http2_parse_headers_block_literal_incindex(input, dyn_headers);
363✔
527
    } else if input[0] & 0x20 != 0 {
87✔
528
        return http2_parse_headers_block_dynamic_size(input, dyn_headers);
8✔
529
    } else if input[0] & 0x10 != 0 {
79✔
530
        return http2_parse_headers_block_literal_neverindex(input, dyn_headers);
×
531
    } else {
532
        return http2_parse_headers_block_literal_noindex(input, dyn_headers);
79✔
533
    }
534
}
1,371✔
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>(
126✔
550
    input: &'a [u8], dyn_headers: &mut HTTP2DynTable,
126✔
551
) -> IResult<&'a [u8], Vec<HTTP2FrameHeaderBlock>> {
126✔
552
    let mut blocks = Vec::new();
126✔
553
    let mut i3 = input;
126✔
554
    while !i3.is_empty() {
1,491✔
555
        match http2_parse_headers_block(i3, dyn_headers) {
1,365✔
556
            Ok((rem, b)) => {
1,365✔
557
                blocks.push(b);
1,365✔
558
                debug_validate_bug_on!(i3.len() == rem.len());
1,365✔
559
                if i3.len() == rem.len() {
1,365✔
560
                    //infinite loop
561
                    return Err(Err::Error(make_error(input, ErrorKind::Eof)));
×
562
                }
1,365✔
563
                i3 = rem;
1,365✔
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
            }
577
            Err(x) => {
×
578
                return Err(x);
×
579
            }
580
        }
581
    }
582
    return Ok((i3, blocks));
126✔
583
}
126✔
584

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

612
pub fn http2_parse_frame_push_promise<'a>(
×
613
    input: &'a [u8], flags: u8, dyn_headers: &mut HTTP2DynTable,
×
614
) -> IResult<&'a [u8], HTTP2FramePushPromise> {
×
615
    let (i2, padlength) = cond(flags & HTTP2_FLAG_HEADER_PADDED != 0, be_u8)(input)?;
×
616
    let (i3, stream_id) = bits(tuple((take_bits(1u8), take_bits(31u32))))(i2)?;
×
617
    let (i3, blocks) = http2_parse_headers_blocks(i3, dyn_headers)?;
×
618
    return Ok((
×
619
        i3,
×
620
        HTTP2FramePushPromise {
×
621
            padlength,
×
622
            reserved: stream_id.0,
×
623
            stream_id: stream_id.1,
×
624
            blocks,
×
625
        },
×
626
    ));
×
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)]
111✔
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 {
111✔
656
        write!(f, "{:?}", self)
111✔
657
    }
111✔
658
}
659

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

663
    fn from_str(s: &str) -> Result<Self, Self::Err> {
7✔
664
        let su = s.to_uppercase();
7✔
665
        let su_slice: &str = &su;
7✔
666
        match su_slice {
7✔
667
            "SETTINGS_HEADER_TABLE_SIZE" => Ok(HTTP2SettingsId::HeaderTableSize),
7✔
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✔
673
            "SETTINGS_ENABLE_CONNECT_PROTOCOL" => Ok(HTTP2SettingsId::EnableConnectProtocol),
×
674
            "SETTINGS_NO_RFC7540_PRIORITIES" => Ok(HTTP2SettingsId::NoRfc7540Priorities),
×
675
            _ => Err(format!("'{}' is not a valid value for HTTP2SettingsId", s)),
×
676
        }
677
    }
7✔
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> {
7✔
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)?;
7✔
692
    let (i, id) = map_opt8(alt8((complete8(is_not8(" <>=")), rest8)), |s: &str| {
7✔
693
        HTTP2SettingsId::from_str(s).ok()
7✔
694
    }).parse(i)?;
7✔
695
    let (i, value) = opt8(complete8(detect_parse_uint)).parse(i)?;
7✔
696
    Ok((i, DetectHTTP2settingsSigCtx { id, value }))
7✔
697
}
7✔
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> {
144✔
706
    let (i, id) = map_opt(be_u16, num::FromPrimitive::from_u16)(i)?;
144✔
707
    let (i, value) = be_u32(i)?;
111✔
708
    Ok((i, HTTP2FrameSettings { id, value }))
111✔
709
}
144✔
710

711
pub fn http2_parse_frame_settings(i: &[u8]) -> IResult<&[u8], Vec<HTTP2FrameSettings>> {
33✔
712
    many0(complete(http2_parse_frame_setting))(i)
33✔
713
}
33✔
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
        _ => {
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