• 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

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

18
use super::http2::{
19
    HTTP2Event, HTTP2Frame, HTTP2FrameTypeData, HTTP2State, HTTP2Transaction, HTTP2TransactionState,
20
};
21
use super::parser;
22
use crate::detect::uint::{
23
    detect_match_uint, detect_parse_array_uint_enum, detect_uint_match_at_index,
24
    DetectUintArrayData, DetectUintData, DetectUintIndex, DetectUintMode,
25
};
26
use crate::detect::EnumString;
27
use crate::direction::Direction;
28
use base64::{engine::general_purpose::STANDARD, Engine};
29
use std::ffi::CStr;
30
use std::os::raw::c_void;
31
use std::rc::Rc;
32
use suricata_sys::sys::DetectEngineThreadCtx;
33

34
#[no_mangle]
35
pub unsafe extern "C" fn SCHttp2TxHasFrametype(
×
36
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
×
37
) -> std::os::raw::c_int {
×
38
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
39
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u8>);
×
40
    let frames = if direction & Direction::ToServer as u8 != 0 {
×
41
        &tx.frames_ts
×
42
    } else {
43
        &tx.frames_tc
×
44
    };
45
    return detect_uint_match_at_index::<HTTP2Frame, u8>(
×
46
        frames,
×
47
        ctx,
×
48
        |f| Some(f.header.ftype),
×
49
        tx.state >= HTTP2TransactionState::HTTP2StateClosed,
×
50
    );
×
51
}
×
52

53
#[no_mangle]
54
pub unsafe extern "C" fn SCHttp2ParseFrametype(
1✔
55
    str: *const std::os::raw::c_char,
1✔
56
) -> *mut std::os::raw::c_void {
1✔
57
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
1✔
58
    if let Ok(s) = ft_name.to_str() {
1✔
59
        if let Some(ctx) = detect_parse_array_uint_enum::<u8, parser::HTTP2FrameType>(s) {
1✔
60
            let boxed = Box::new(ctx);
1✔
61
            // DetectUintArrayData<u8> cannot be cbindgend
1✔
62
            return Box::into_raw(boxed) as *mut c_void;
1✔
63
        }
×
64
    }
×
65
    return std::ptr::null_mut();
×
66
}
1✔
67

68
fn http2_tx_get_errorcode(f: &HTTP2Frame) -> Option<u32> {
×
69
    match &f.data {
×
70
        HTTP2FrameTypeData::GOAWAY(goaway) => Some(goaway.errorcode),
×
71
        HTTP2FrameTypeData::RSTSTREAM(rst) => Some(rst.errorcode),
×
72
        _ => None,
×
73
    }
74
}
×
75

76
#[no_mangle]
77
pub unsafe extern "C" fn SCHttp2TxHasErrorCode(
×
78
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
×
79
) -> std::os::raw::c_int {
×
80
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
81
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u32>);
×
82
    let frames = if direction & Direction::ToServer as u8 != 0 {
×
83
        &tx.frames_ts
×
84
    } else {
85
        &tx.frames_tc
×
86
    };
87
    return detect_uint_match_at_index::<HTTP2Frame, u32>(
×
88
        frames,
×
89
        ctx,
×
90
        http2_tx_get_errorcode,
×
91
        tx.state >= HTTP2TransactionState::HTTP2StateClosed,
×
92
    );
×
93
}
×
94

95
#[no_mangle]
96
pub unsafe extern "C" fn SCHttp2ParseErrorCode(
1✔
97
    str: *const std::os::raw::c_char,
1✔
98
) -> *mut std::os::raw::c_void {
1✔
99
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
1✔
100
    if let Ok(s) = ft_name.to_str() {
1✔
101
        // special case for backward compatibility, now parsed as HTTP11_REQUIRED
102
        if s.to_uppercase() == "HTTP_1_1_REQUIRED" {
1✔
103
            let ctx = DetectUintArrayData::<u32> {
×
104
                du: DetectUintData {
×
105
                    arg1: parser::HTTP2ErrorCode::Http11Required.into_u(),
×
106
                    arg2: 0,
×
107
                    mode: DetectUintMode::DetectUintModeEqual,
×
108
                },
×
109
                index: DetectUintIndex::Any,
×
110
                start: 0,
×
111
                end: 0,
×
112
            };
×
113
            let boxed = Box::new(ctx);
×
114
            return Box::into_raw(boxed) as *mut c_void;
×
115
        }
1✔
116
        if let Some(ctx) = detect_parse_array_uint_enum::<u32, parser::HTTP2ErrorCode>(s) {
1✔
117
            let boxed = Box::new(ctx);
1✔
118
            return Box::into_raw(boxed) as *mut c_void;
1✔
119
        }
×
120
    }
×
121
    return std::ptr::null_mut();
×
122
}
1✔
123

124
fn get_http2_priority(frame: &HTTP2Frame) -> Option<u8> {
×
125
    return match &frame.data {
×
126
        HTTP2FrameTypeData::PRIORITY(prio) => Some(prio.weight),
×
127
        HTTP2FrameTypeData::HEADERS(hd) => {
×
128
            if let Some(prio) = hd.priority {
×
129
                return Some(prio.weight);
×
130
            }
×
131
            None
×
132
        }
133
        _ => None,
×
134
    };
135
}
×
136

137
fn http2_match_priority(
×
138
    tx: &HTTP2Transaction, direction: Direction, ctx: &DetectUintArrayData<u8>,
×
139
) -> std::os::raw::c_int {
×
140
    let frames = if direction == Direction::ToServer {
×
141
        &tx.frames_ts
×
142
    } else {
143
        &tx.frames_tc
×
144
    };
145
    let eof = tx.state >= HTTP2TransactionState::HTTP2StateClosed;
×
146
    return detect_uint_match_at_index::<HTTP2Frame, u8>(frames, ctx, get_http2_priority, eof);
×
147
}
×
148

149
#[no_mangle]
150
pub unsafe extern "C" fn SCHttp2PriorityMatch(
×
151
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
×
152
) -> std::os::raw::c_int {
×
153
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
154
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u8>);
×
155
    return http2_match_priority(tx, direction.into(), ctx);
×
156
}
×
157

158
fn get_http2_window(frame: &HTTP2Frame) -> Option<u32> {
×
159
    if let HTTP2FrameTypeData::WINDOWUPDATE(wu) = &frame.data {
×
160
        return Some(wu.sizeinc);
×
161
    }
×
162
    return None;
×
163
}
×
164

165
fn http2_match_window(
×
166
    tx: &HTTP2Transaction, direction: Direction, ctx: &DetectUintArrayData<u32>,
×
167
) -> std::os::raw::c_int {
×
168
    let frames = if direction == Direction::ToServer {
×
169
        &tx.frames_ts
×
170
    } else {
171
        &tx.frames_tc
×
172
    };
173
    let eof = tx.state >= HTTP2TransactionState::HTTP2StateClosed;
×
174
    return detect_uint_match_at_index::<HTTP2Frame, u32>(frames, ctx, get_http2_window, eof);
×
175
}
×
176

177
#[no_mangle]
178
pub unsafe extern "C" fn SCHttp2WindowMatch(
×
179
    tx: *mut std::os::raw::c_void, direction: u8, ctx: *const std::os::raw::c_void,
×
180
) -> std::os::raw::c_int {
×
181
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
182
    let ctx = cast_pointer!(ctx, DetectUintArrayData<u32>);
×
183
    return http2_match_window(tx, direction.into(), ctx);
×
184
}
×
185

186
#[no_mangle]
187
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxParse(
1✔
188
    str: *const std::os::raw::c_char,
1✔
189
) -> *mut std::os::raw::c_void {
1✔
190
    let ft_name: &CStr = CStr::from_ptr(str); //unsafe
1✔
191
    if let Ok(s) = ft_name.to_str() {
1✔
192
        if let Ok((_, ctx)) = parser::http2_parse_settingsctx(s) {
1✔
193
            let boxed = Box::new(ctx);
1✔
194
            return Box::into_raw(boxed) as *mut _;
1✔
195
        }
×
196
    }
×
197
    return std::ptr::null_mut();
×
198
}
1✔
199

200
#[no_mangle]
201
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxFree(ctx: *mut std::os::raw::c_void) {
1✔
202
    // Just unbox...
1✔
203
    std::mem::drop(Box::from_raw(ctx as *mut parser::DetectHTTP2settingsSigCtx));
1✔
204
}
1✔
205

206
fn http2_detect_settings_match(
×
207
    set: &[parser::HTTP2FrameSettings], ctx: &parser::DetectHTTP2settingsSigCtx,
×
208
) -> std::os::raw::c_int {
×
209
    for e in set {
×
210
        if e.id == ctx.id {
×
211
            match &ctx.value {
×
212
                None => {
213
                    return 1;
×
214
                }
215
                Some(x) => {
×
216
                    if detect_match_uint(x, e.value) {
×
217
                        return 1;
×
218
                    }
×
219
                }
220
            }
221
        }
×
222
    }
223
    return 0;
×
224
}
×
225

226
fn http2_detect_settingsctx_match(
×
227
    ctx: &parser::DetectHTTP2settingsSigCtx, tx: &HTTP2Transaction, direction: Direction,
×
228
) -> std::os::raw::c_int {
×
229
    if direction == Direction::ToServer {
×
230
        for i in 0..tx.frames_ts.len() {
×
231
            if let HTTP2FrameTypeData::SETTINGS(set) = &tx.frames_ts[i].data {
×
232
                if http2_detect_settings_match(set, ctx) != 0 {
×
233
                    return 1;
×
234
                }
×
235
            }
×
236
        }
237
    } else {
238
        for i in 0..tx.frames_tc.len() {
×
239
            if let HTTP2FrameTypeData::SETTINGS(set) = &tx.frames_tc[i].data {
×
240
                if http2_detect_settings_match(set, ctx) != 0 {
×
241
                    return 1;
×
242
                }
×
243
            }
×
244
        }
245
    }
246
    return 0;
×
247
}
×
248

249
#[no_mangle]
250
pub unsafe extern "C" fn SCHttp2DetectSettingsCtxMatch(
×
251
    ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
×
252
) -> std::os::raw::c_int {
×
253
    let ctx = cast_pointer!(ctx, parser::DetectHTTP2settingsSigCtx);
×
254
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
255
    return http2_detect_settingsctx_match(ctx, tx, direction.into());
×
256
}
×
257

258
fn http2_detect_sizeupdate_match(
×
259
    blocks: &[parser::HTTP2FrameHeaderBlock], ctx: &DetectUintData<u64>,
×
260
) -> std::os::raw::c_int {
×
261
    for block in blocks.iter() {
×
262
        if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
×
263
            && detect_match_uint(ctx, block.sizeupdate)
×
264
        {
265
            return 1;
×
266
        }
×
267
    }
268
    return 0;
×
269
}
×
270

271
fn http2_header_blocks(frame: &HTTP2Frame) -> Option<&[parser::HTTP2FrameHeaderBlock]> {
10,195✔
272
    match &frame.data {
10,195✔
273
        HTTP2FrameTypeData::HEADERS(hd) => {
2,671✔
274
            return Some(&hd.blocks);
2,671✔
275
        }
276
        HTTP2FrameTypeData::CONTINUATION(hd) => {
36✔
277
            return Some(&hd.blocks);
36✔
278
        }
279
        HTTP2FrameTypeData::PUSHPROMISE(hd) => {
×
280
            return Some(&hd.blocks);
×
281
        }
282
        _ => {}
7,488✔
283
    }
7,488✔
284
    return None;
7,488✔
285
}
10,195✔
286

287
fn http2_detect_sizeupdatectx_match(
×
288
    ctx: &DetectUintData<u64>, tx: &HTTP2Transaction, direction: Direction,
×
289
) -> std::os::raw::c_int {
×
290
    if direction == Direction::ToServer {
×
291
        for i in 0..tx.frames_ts.len() {
×
292
            if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
×
293
                if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
×
294
                    return 1;
×
295
                }
×
296
            }
×
297
        }
298
    } else {
299
        for i in 0..tx.frames_tc.len() {
×
300
            if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
×
301
                if http2_detect_sizeupdate_match(blocks, ctx) != 0 {
×
302
                    return 1;
×
303
                }
×
304
            }
×
305
        }
306
    }
307
    return 0;
×
308
}
×
309

310
#[no_mangle]
311
pub unsafe extern "C" fn SCHttp2DetectSizeUpdateCtxMatch(
×
312
    ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
×
313
) -> std::os::raw::c_int {
×
314
    let ctx = cast_pointer!(ctx, DetectUintData<u64>);
×
315
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
316
    return http2_detect_sizeupdatectx_match(ctx, tx, direction.into());
×
317
}
×
318

319
//TODOask better syntax between SCHttp2TxGetHeaderName in argument
320
// and SCHttp2DetectSizeUpdateCtxMatch explicitly casting
321
#[no_mangle]
322
pub unsafe extern "C" fn SCHttp2TxGetHeaderName(
×
323
    _de: *mut DetectEngineThreadCtx, tx: *const c_void, direction: u8, nb: u32,
×
324
    buffer: *mut *const u8, buffer_len: *mut u32,
×
325
) -> bool {
×
326
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
327
    let mut pos = 0_u32;
×
328
    match direction.into() {
×
329
        Direction::ToServer => {
330
            for i in 0..tx.frames_ts.len() {
×
331
                if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
×
332
                    if nb < pos + blocks.len() as u32 {
×
333
                        let value = &blocks[(nb - pos) as usize].name;
×
334
                        *buffer = value.as_ptr(); //unsafe
×
335
                        *buffer_len = value.len() as u32;
×
336
                        return true;
×
337
                    } else {
×
338
                        pos += blocks.len() as u32;
×
339
                    }
×
340
                }
×
341
            }
342
        }
343
        Direction::ToClient => {
344
            for i in 0..tx.frames_tc.len() {
×
345
                if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
×
346
                    if nb < pos + blocks.len() as u32 {
×
347
                        let value = &blocks[(nb - pos) as usize].name;
×
348
                        *buffer = value.as_ptr(); //unsafe
×
349
                        *buffer_len = value.len() as u32;
×
350
                        return true;
×
351
                    } else {
×
352
                        pos += blocks.len() as u32;
×
353
                    }
×
354
                }
×
355
            }
356
        }
357
    }
358
    return false;
×
359
}
×
360

361
fn http2_frames_get_header_firstvalue<'a>(
1,181✔
362
    tx: &'a mut HTTP2Transaction, direction: Direction, name: &str,
1,181✔
363
) -> Result<&'a [u8], ()> {
1,181✔
364
    let frames = if direction == Direction::ToServer {
1,181✔
365
        &tx.frames_ts
830✔
366
    } else {
367
        &tx.frames_tc
351✔
368
    };
369
    for frame in frames {
1,566✔
370
        if let Some(blocks) = http2_header_blocks(frame) {
873✔
371
            for block in blocks.iter() {
853✔
372
                if block.name.as_ref() == name.as_bytes() {
853✔
373
                    return Ok(&block.value);
488✔
374
                }
365✔
375
            }
376
        }
360✔
377
    }
378
    return Err(());
693✔
379
}
1,181✔
380

381
// same as http2_frames_get_header_value but returns a new Vec
382
// instead of using the transaction to store the result slice
383
pub fn http2_frames_get_header_value_vec(
70✔
384
    tx: &HTTP2Transaction, direction: Direction, name: &str,
70✔
385
) -> Result<Vec<u8>, ()> {
70✔
386
    let mut found = 0;
70✔
387
    let mut vec = Vec::new();
70✔
388
    let frames = if direction == Direction::ToServer {
70✔
389
        &tx.frames_ts
6✔
390
    } else {
391
        &tx.frames_tc
64✔
392
    };
393
    for frame in frames {
246✔
394
        if let Some(blocks) = http2_header_blocks(frame) {
176✔
395
            for block in blocks.iter() {
688✔
396
                if block.name.as_ref() == name.as_bytes() {
688✔
397
                    if found == 0 {
9✔
398
                        vec.extend_from_slice(&block.value);
9✔
399
                        found = 1;
9✔
400
                    } else if found == 1 && Rc::strong_count(&block.name) <= 2 {
9✔
401
                        vec.extend_from_slice(b", ");
×
402
                        vec.extend_from_slice(&block.value);
×
403
                        found = 2;
×
404
                    } else if Rc::strong_count(&block.name) <= 2 {
×
405
                        vec.extend_from_slice(b", ");
×
406
                        vec.extend_from_slice(&block.value);
×
407
                    }
×
408
                }
679✔
409
            }
410
        }
64✔
411
    }
412
    if found == 0 {
70✔
413
        return Err(());
61✔
414
    } else {
415
        return Ok(vec);
9✔
416
    }
417
}
70✔
418

419
fn http2_frames_get_header_value<'a>(
2,650✔
420
    tx: &'a mut HTTP2Transaction, direction: Direction, name: &str,
2,650✔
421
) -> Result<&'a [u8], ()> {
2,650✔
422
    let mut found = 0;
2,650✔
423
    let mut vec = Vec::new();
2,650✔
424
    let mut single: Result<&[u8], ()> = Err(());
2,650✔
425
    let frames = if direction == Direction::ToServer {
2,650✔
426
        &tx.frames_ts
1,710✔
427
    } else {
428
        &tx.frames_tc
940✔
429
    };
430
    for frame in frames {
8,805✔
431
        if let Some(blocks) = http2_header_blocks(frame) {
6,155✔
432
            for block in blocks.iter() {
12,083✔
433
                if block.name.as_ref() == name.as_bytes() {
12,083✔
434
                    if found == 0 {
735✔
435
                        single = Ok(&block.value);
714✔
436
                        found = 1;
714✔
437
                    } else if found == 1 && Rc::strong_count(&block.name) <= 2 {
714✔
438
                        if let Ok(s) = single {
8✔
439
                            vec.extend_from_slice(s);
8✔
440
                        }
8✔
441
                        vec.extend_from_slice(b", ");
8✔
442
                        vec.extend_from_slice(&block.value);
8✔
443
                        found = 2;
8✔
444
                    } else if Rc::strong_count(&block.name) <= 2 {
13✔
445
                        vec.extend_from_slice(b", ");
3✔
446
                        vec.extend_from_slice(&block.value);
3✔
447
                    }
10✔
448
                }
11,348✔
449
            }
450
        }
4,655✔
451
    }
452
    if found == 0 {
2,650✔
453
        return Err(());
1,936✔
454
    } else if found == 1 {
714✔
455
        return single;
706✔
456
    } else {
457
        tx.escaped.push(vec);
8✔
458
        let idx = tx.escaped.len() - 1;
8✔
459
        let value = &tx.escaped[idx];
8✔
460
        return Ok(value);
8✔
461
    }
462
}
2,650✔
463

464
// we mutate the tx to cache req_line
465
fn http2_tx_get_req_line(tx: &mut HTTP2Transaction) {
168✔
466
    if !tx.req_line.is_empty() {
168✔
467
        return;
5✔
468
    }
163✔
469
    let empty = Vec::new();
163✔
470
    let mut req_line = Vec::new();
163✔
471
    let method =
163✔
472
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":method") {
163✔
473
            value
64✔
474
        } else {
475
            &empty
99✔
476
        };
477
    req_line.extend(method);
163✔
478
    req_line.push(b' ');
163✔
479

480
    let uri =
163✔
481
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":path") {
163✔
482
            value
64✔
483
        } else {
484
            &empty
99✔
485
        };
486
    req_line.extend(uri);
163✔
487
    req_line.extend(b" HTTP/2\r\n");
163✔
488
    tx.req_line.extend(req_line)
163✔
489
}
168✔
490

491
#[no_mangle]
492
pub unsafe extern "C" fn SCHttp2TxGetRequestLine(
168✔
493
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
168✔
494
) -> u8 {
168✔
495
    http2_tx_get_req_line(tx);
168✔
496
    *buffer = tx.req_line.as_ptr(); //unsafe
168✔
497
    *buffer_len = tx.req_line.len() as u32;
168✔
498
    return 1;
168✔
499
}
168✔
500

501
fn http2_tx_get_resp_line(tx: &mut HTTP2Transaction) {
188✔
502
    if !tx.resp_line.is_empty() {
188✔
503
        return;
25✔
504
    }
163✔
505
    let empty = Vec::new();
163✔
506
    let mut resp_line: Vec<u8> = Vec::new();
163✔
507

508
    let status =
163✔
509
        if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToClient, ":status") {
163✔
510
            value
64✔
511
        } else {
512
            &empty
99✔
513
        };
514
    resp_line.extend(b"HTTP/2 ");
163✔
515
    resp_line.extend(status);
163✔
516
    resp_line.extend(b"\r\n");
163✔
517
    tx.resp_line.extend(resp_line)
163✔
518
}
188✔
519

520
#[no_mangle]
521
pub unsafe extern "C" fn SCHttp2TxGetResponseLine(
188✔
522
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
188✔
523
) -> u8 {
188✔
524
    http2_tx_get_resp_line(tx);
188✔
525
    *buffer = tx.resp_line.as_ptr(); //unsafe
188✔
526
    *buffer_len = tx.resp_line.len() as u32;
188✔
527
    return 1;
188✔
528
}
188✔
529

530
#[no_mangle]
531
pub unsafe extern "C" fn SCHttp2TxGetUri(
336✔
532
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
336✔
533
) -> u8 {
336✔
534
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":path") {
336✔
535
        *buffer = value.as_ptr(); //unsafe
138✔
536
        *buffer_len = value.len() as u32;
138✔
537
        return 1;
138✔
538
    }
198✔
539
    return 0;
198✔
540
}
336✔
541

542
#[no_mangle]
543
pub unsafe extern "C" fn SCHttp2TxGetMethod(
168✔
544
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
168✔
545
) -> u8 {
168✔
546
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToServer, ":method") {
168✔
547
        *buffer = value.as_ptr(); //unsafe
69✔
548
        *buffer_len = value.len() as u32;
69✔
549
        return 1;
69✔
550
    }
99✔
551
    return 0;
99✔
552
}
168✔
553

554
#[no_mangle]
555
pub unsafe extern "C" fn SCHttp2TxGetHost(
28✔
556
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
28✔
557
) -> u8 {
28✔
558
    if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
28✔
559
        *buffer = value.as_ptr(); //unsafe
9✔
560
        *buffer_len = value.len() as u32;
9✔
561
        return 1;
9✔
562
    }
19✔
563
    return 0;
19✔
564
}
28✔
565

566
fn http2_lower(value: &[u8]) -> Option<Vec<u8>> {
138✔
567
    for i in 0..value.len() {
2,524✔
568
        if value[i].is_ascii_uppercase() {
2,524✔
569
            // we got at least one upper character, need to transform
570
            let mut vec: Vec<u8> = Vec::with_capacity(value.len());
×
571
            vec.extend_from_slice(value);
×
572
            for e in &mut vec {
×
573
                e.make_ascii_lowercase();
×
574
            }
×
575
            return Some(vec);
×
576
        }
2,524✔
577
    }
578
    return None;
138✔
579
}
138✔
580

581
// returns a tuple with the value and its size
582
fn http2_normalize_host(value: &[u8]) -> &[u8] {
143✔
583
    match value.iter().position(|&x| x == b'@') {
2,816✔
584
        Some(i) => {
4✔
585
            let value = &value[i + 1..];
4✔
586
            match value.iter().position(|&x| x == b':') {
39✔
587
                Some(i) => {
1✔
588
                    return &value[..i];
1✔
589
                }
590
                None => {
591
                    return value;
3✔
592
                }
593
            }
594
        }
595
        None => match value.iter().position(|&x| x == b':') {
2,568✔
596
            Some(i) => {
40✔
597
                return &value[..i];
40✔
598
            }
599
            None => {
600
                return value;
99✔
601
            }
602
        },
603
    }
604
}
143✔
605

606
#[no_mangle]
607
pub unsafe extern "C" fn SCHttp2TxGetHostNorm(
336✔
608
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
336✔
609
) -> u8 {
336✔
610
    if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, ":authority") {
336✔
611
        let r = http2_normalize_host(value);
138✔
612
        // r is a tuple with the value and its size
138✔
613
        // this is useful when we only take a substring (before the port)
138✔
614
        match http2_lower(r) {
138✔
615
            Some(normval) => {
×
616
                // In case we needed some normalization,
×
617
                // the transaction needs to take ownership of this normalized host
×
618
                tx.escaped.push(normval);
×
619
                let idx = tx.escaped.len() - 1;
×
620
                let resvalue = &tx.escaped[idx];
×
621
                *buffer = resvalue.as_ptr(); //unsafe
×
622
                *buffer_len = resvalue.len() as u32;
×
623
                return 1;
×
624
            }
625
            None => {
626
                *buffer = r.as_ptr(); //unsafe
138✔
627
                *buffer_len = r.len() as u32;
138✔
628
                return 1;
138✔
629
            }
630
        }
631
    }
198✔
632
    return 0;
198✔
633
}
336✔
634

635
#[no_mangle]
636
pub unsafe extern "C" fn SCHttp2TxGetUserAgent(
168✔
637
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
168✔
638
) -> u8 {
168✔
639
    if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "user-agent") {
168✔
640
        *buffer = value.as_ptr(); //unsafe
65✔
641
        *buffer_len = value.len() as u32;
65✔
642
        return 1;
65✔
643
    }
103✔
644
    return 0;
103✔
645
}
168✔
646

647
#[no_mangle]
648
pub unsafe extern "C" fn SCHttp2TxGetStatus(
188✔
649
    tx: &mut HTTP2Transaction, buffer: *mut *const u8, buffer_len: *mut u32,
188✔
650
) -> u8 {
188✔
651
    if let Ok(value) = http2_frames_get_header_firstvalue(tx, Direction::ToClient, ":status") {
188✔
652
        *buffer = value.as_ptr(); //unsafe
89✔
653
        *buffer_len = value.len() as u32;
89✔
654
        return 1;
89✔
655
    }
99✔
656
    return 0;
99✔
657
}
188✔
658

659
#[no_mangle]
660
pub unsafe extern "C" fn SCHttp2TxGetCookie(
356✔
661
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
356✔
662
) -> u8 {
356✔
663
    if direction == u8::from(Direction::ToServer) {
356✔
664
        if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToServer, "cookie") {
168✔
665
            *buffer = value.as_ptr(); //unsafe
9✔
666
            *buffer_len = value.len() as u32;
9✔
667
            return 1;
9✔
668
        }
159✔
669
    } else if let Ok(value) = http2_frames_get_header_value(tx, Direction::ToClient, "set-cookie") {
188✔
670
        *buffer = value.as_ptr(); //unsafe
1✔
671
        *buffer_len = value.len() as u32;
1✔
672
        return 1;
1✔
673
    }
187✔
674
    return 0;
346✔
675
}
356✔
676

677
#[no_mangle]
678
pub unsafe extern "C" fn SCHttp2TxGetHeaderValue(
1,761✔
679
    tx: &mut HTTP2Transaction, direction: u8, strname: *const std::os::raw::c_char,
1,761✔
680
    buffer: *mut *const u8, buffer_len: *mut u32,
1,761✔
681
) -> u8 {
1,761✔
682
    let hname: &CStr = CStr::from_ptr(strname); //unsafe
1,761✔
683
    if let Ok(s) = hname.to_str() {
1,761✔
684
        if let Ok(value) = http2_frames_get_header_value(tx, direction.into(), &s.to_lowercase()) {
1,761✔
685
            *buffer = value.as_ptr(); //unsafe
491✔
686
            *buffer_len = value.len() as u32;
491✔
687
            return 1;
491✔
688
        }
1,270✔
689
    }
×
690
    return 0;
1,270✔
691
}
1,761✔
692

693
fn http2_escape_header(blocks: &[parser::HTTP2FrameHeaderBlock], i: u32) -> Vec<u8> {
×
694
    //minimum size + 2 for escapes
×
695
    let normalsize = blocks[i as usize].value.len() + 2 + blocks[i as usize].name.len();
×
696
    let mut vec = Vec::with_capacity(normalsize);
×
697
    vec.extend_from_slice(&blocks[i as usize].name);
×
698
    vec.extend_from_slice(b": ");
×
699
    vec.extend_from_slice(&blocks[i as usize].value);
×
700
    return vec;
×
701
}
×
702

703
#[no_mangle]
704
pub unsafe extern "C" fn SCHttp2TxGetHeaderNames(
356✔
705
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
356✔
706
) -> u8 {
356✔
707
    let mut vec = vec![b'\r', b'\n'];
356✔
708
    let frames = if direction & Direction::ToServer as u8 != 0 {
356✔
709
        &tx.frames_ts
168✔
710
    } else {
711
        &tx.frames_tc
188✔
712
    };
713
    for frame in frames {
1,353✔
714
        if let Some(blocks) = http2_header_blocks(frame) {
997✔
715
            for block in blocks.iter() {
1,602✔
716
                // we do not escape linefeeds in headers names
1,602✔
717
                vec.extend_from_slice(&block.name);
1,602✔
718
                vec.extend_from_slice(b"\r\n");
1,602✔
719
            }
1,602✔
720
        }
803✔
721
    }
722
    if vec.len() > 2 {
356✔
723
        vec.extend_from_slice(b"\r\n");
158✔
724
        tx.escaped.push(vec);
158✔
725
        let idx = tx.escaped.len() - 1;
158✔
726
        let value = &tx.escaped[idx];
158✔
727
        *buffer = value.as_ptr(); //unsafe
158✔
728
        *buffer_len = value.len() as u32;
158✔
729
        return 1;
158✔
730
    }
198✔
731
    return 0;
198✔
732
}
356✔
733

734
fn http2_header_iscookie(direction: Direction, hname: &[u8]) -> bool {
1,602✔
735
    if let Ok(s) = std::str::from_utf8(hname) {
1,602✔
736
        if direction == Direction::ToServer {
1,602✔
737
            if s.to_lowercase() == "cookie" {
798✔
738
                return true;
26✔
739
            }
772✔
740
        } else if s.to_lowercase() == "set-cookie" {
804✔
741
            return true;
1✔
742
        }
803✔
743
    }
×
744
    return false;
1,575✔
745
}
1,602✔
746

747
fn http2_header_trimspaces(value: &[u8]) -> &[u8] {
1,578✔
748
    let mut start = 0;
1,578✔
749
    let mut end = value.len();
1,578✔
750
    while start < value.len() {
1,581✔
751
        if value[start] == b' ' || value[start] == b'\t' {
1,560✔
752
            start += 1;
3✔
753
        } else {
3✔
754
            break;
1,557✔
755
        }
756
    }
757
    while end > start {
1,579✔
758
        if value[end - 1] == b' ' || value[end - 1] == b'\t' {
1,558✔
759
            end -= 1;
1✔
760
        } else {
1✔
761
            break;
1,557✔
762
        }
763
    }
764
    return &value[start..end];
1,578✔
765
}
1,578✔
766

767
#[no_mangle]
768
pub unsafe extern "C" fn SCHttp2TxGetHeaders(
356✔
769
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
356✔
770
) -> u8 {
356✔
771
    let mut vec = Vec::new();
356✔
772
    let frames = if direction & Direction::ToServer as u8 != 0 {
356✔
773
        &tx.frames_ts
168✔
774
    } else {
775
        &tx.frames_tc
188✔
776
    };
777
    for frame in frames {
1,353✔
778
        if let Some(blocks) = http2_header_blocks(frame) {
997✔
779
            for block in blocks.iter() {
1,602✔
780
                if !http2_header_iscookie(direction.into(), &block.name) {
1,602✔
781
                    // we do not escape linefeeds nor : in headers names
1,575✔
782
                    vec.extend_from_slice(&block.name);
1,575✔
783
                    vec.extend_from_slice(b": ");
1,575✔
784
                    vec.extend_from_slice(http2_header_trimspaces(&block.value));
1,575✔
785
                    vec.extend_from_slice(b"\r\n");
1,575✔
786
                }
1,575✔
787
            }
788
        }
803✔
789
    }
790
    if !vec.is_empty() {
356✔
791
        tx.escaped.push(vec);
158✔
792
        let idx = tx.escaped.len() - 1;
158✔
793
        let value = &tx.escaped[idx];
158✔
794
        *buffer = value.as_ptr(); //unsafe
158✔
795
        *buffer_len = value.len() as u32;
158✔
796
        return 1;
158✔
797
    }
198✔
798
    return 0;
198✔
799
}
356✔
800

801
#[no_mangle]
802
pub unsafe extern "C" fn SCHttp2TxGetHeadersRaw(
356✔
803
    tx: &mut HTTP2Transaction, direction: u8, buffer: *mut *const u8, buffer_len: *mut u32,
356✔
804
) -> u8 {
356✔
805
    let mut vec = Vec::new();
356✔
806
    let frames = if direction & Direction::ToServer as u8 != 0 {
356✔
807
        &tx.frames_ts
168✔
808
    } else {
809
        &tx.frames_tc
188✔
810
    };
811
    for frame in frames {
1,353✔
812
        if let Some(blocks) = http2_header_blocks(frame) {
997✔
813
            for block in blocks.iter() {
1,602✔
814
                // we do not escape linefeeds nor : in headers names
1,602✔
815
                vec.extend_from_slice(&block.name);
1,602✔
816
                vec.extend_from_slice(b": ");
1,602✔
817
                vec.extend_from_slice(&block.value);
1,602✔
818
                vec.extend_from_slice(b"\r\n");
1,602✔
819
            }
1,602✔
820
        }
803✔
821
    }
822
    if !vec.is_empty() {
356✔
823
        tx.escaped.push(vec);
158✔
824
        let idx = tx.escaped.len() - 1;
158✔
825
        let value = &tx.escaped[idx];
158✔
826
        *buffer = value.as_ptr(); //unsafe
158✔
827
        *buffer_len = value.len() as u32;
158✔
828
        return 1;
158✔
829
    }
198✔
830
    return 0;
198✔
831
}
356✔
832

833
#[no_mangle]
834
pub unsafe extern "C" fn SCHttp2TxGetHeader(
×
835
    _de: *mut DetectEngineThreadCtx, tx: *const c_void, direction: u8, nb: u32,
×
836
    buffer: *mut *const u8, buffer_len: *mut u32,
×
837
) -> bool {
×
838
    let tx = cast_pointer!(tx, HTTP2Transaction);
×
839
    let mut pos = 0_u32;
×
840
    match direction.into() {
×
841
        Direction::ToServer => {
842
            for i in 0..tx.frames_ts.len() {
×
843
                if let Some(blocks) = http2_header_blocks(&tx.frames_ts[i]) {
×
844
                    if nb < pos + blocks.len() as u32 {
×
845
                        let ehdr = http2_escape_header(blocks, nb - pos);
×
846
                        tx.escaped.push(ehdr);
×
847
                        let idx = tx.escaped.len() - 1;
×
848
                        let value = &tx.escaped[idx];
×
849
                        *buffer = value.as_ptr(); //unsafe
×
850
                        *buffer_len = value.len() as u32;
×
851
                        return true;
×
852
                    } else {
×
853
                        pos += blocks.len() as u32;
×
854
                    }
×
855
                }
×
856
            }
857
        }
858
        Direction::ToClient => {
859
            for i in 0..tx.frames_tc.len() {
×
860
                if let Some(blocks) = http2_header_blocks(&tx.frames_tc[i]) {
×
861
                    if nb < pos + blocks.len() as u32 {
×
862
                        let ehdr = http2_escape_header(blocks, nb - pos);
×
863
                        tx.escaped.push(ehdr);
×
864
                        let idx = tx.escaped.len() - 1;
×
865
                        let value = &tx.escaped[idx];
×
866
                        *buffer = value.as_ptr(); //unsafe
×
867
                        *buffer_len = value.len() as u32;
×
868
                        return true;
×
869
                    } else {
×
870
                        pos += blocks.len() as u32;
×
871
                    }
×
872
                }
×
873
            }
874
        }
875
    }
876
    return false;
×
877
}
×
878

879
fn http2_tx_set_header(state: &mut HTTP2State, name: &[u8], input: &[u8]) {
36✔
880
    let head = parser::HTTP2FrameHeader {
36✔
881
        length: 0,
36✔
882
        ftype: parser::HTTP2FrameType::Headers as u8,
36✔
883
        flags: 0,
36✔
884
        reserved: 0,
36✔
885
        stream_id: 1,
36✔
886
    };
36✔
887
    let mut blocks = Vec::new();
36✔
888
    let b = parser::HTTP2FrameHeaderBlock {
36✔
889
        name: Rc::new(name.to_vec()),
36✔
890
        value: Rc::new(input.to_vec()),
36✔
891
        error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
36✔
892
        sizeupdate: 0,
36✔
893
    };
36✔
894
    blocks.push(b);
36✔
895
    let hs = parser::HTTP2FrameHeaders {
36✔
896
        padlength: None,
36✔
897
        priority: None,
36✔
898
        blocks,
36✔
899
    };
36✔
900
    let txdata = HTTP2FrameTypeData::HEADERS(hs);
36✔
901
    let tx = state
36✔
902
        .find_or_create_tx(&head, &txdata, Direction::ToServer)
36✔
903
        .unwrap();
36✔
904
    tx.frames_ts.push(HTTP2Frame {
36✔
905
        header: head,
36✔
906
        data: txdata,
36✔
907
    });
36✔
908
    //we do not expect more data from client
36✔
909
    tx.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
36✔
910
}
36✔
911

912
#[no_mangle]
913
pub unsafe extern "C" fn SCHttp2TxSetMethod(
5✔
914
    state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
5✔
915
) {
5✔
916
    let slice = build_slice!(buffer, buffer_len as usize);
5✔
917
    http2_tx_set_header(state, ":method".as_bytes(), slice)
5✔
918
}
5✔
919

920
#[no_mangle]
921
pub unsafe extern "C" fn SCHttp2TxSetUri(
5✔
922
    state: &mut HTTP2State, buffer: *const u8, buffer_len: u32,
5✔
923
) {
5✔
924
    let slice = build_slice!(buffer, buffer_len as usize);
5✔
925
    http2_tx_set_header(state, ":path".as_bytes(), slice)
5✔
926
}
5✔
927

928
fn http2_tx_set_settings(state: &mut HTTP2State, input: &[u8]) {
4✔
929
    match STANDARD.decode(input) {
4✔
930
        Ok(dec) => {
4✔
931
            if dec.len() % 6 != 0 {
4✔
932
                state.set_event(HTTP2Event::InvalidHttp1Settings);
×
933
            }
4✔
934

935
            let head = parser::HTTP2FrameHeader {
4✔
936
                length: dec.len() as u32,
4✔
937
                ftype: parser::HTTP2FrameType::Settings as u8,
4✔
938
                flags: 0,
4✔
939
                reserved: 0,
4✔
940
                stream_id: 0,
4✔
941
            };
4✔
942

4✔
943
            match parser::http2_parse_frame_settings(&dec) {
4✔
944
                Ok((_, set)) => {
4✔
945
                    let txdata = HTTP2FrameTypeData::SETTINGS(set);
4✔
946
                    let tx = state
4✔
947
                        .find_or_create_tx(&head, &txdata, Direction::ToServer)
4✔
948
                        .unwrap();
4✔
949
                    tx.frames_ts.push(HTTP2Frame {
4✔
950
                        header: head,
4✔
951
                        data: txdata,
4✔
952
                    });
4✔
953
                }
4✔
954
                Err(_) => {
×
955
                    state.set_event(HTTP2Event::InvalidHttp1Settings);
×
956
                }
×
957
            }
958
        }
959
        Err(_) => {
×
960
            state.set_event(HTTP2Event::InvalidHttp1Settings);
×
961
        }
×
962
    }
963
}
4✔
964

965
fn http2_caseinsensitive_cmp(s1: &[u8], s2: &str) -> bool {
26✔
966
    if let Ok(s) = std::str::from_utf8(s1) {
26✔
967
        return s.to_lowercase() == s2;
26✔
968
    }
×
969
    return false;
×
970
}
26✔
971

972
#[no_mangle]
973
pub unsafe extern "C" fn SCHttp2TxAddHeader(
30✔
974
    state: &mut HTTP2State, name: *const u8, name_len: u32, value: *const u8, value_len: u32,
30✔
975
) {
30✔
976
    let slice_name = build_slice!(name, name_len as usize);
30✔
977
    let slice_value = build_slice!(value, value_len as usize);
30✔
978
    if slice_name == "HTTP2-Settings".as_bytes() {
30✔
979
        http2_tx_set_settings(state, slice_value)
4✔
980
    } else if http2_caseinsensitive_cmp(slice_name, "host") {
26✔
981
        http2_tx_set_header(state, ":authority".as_bytes(), slice_value)
5✔
982
    } else {
983
        http2_tx_set_header(state, slice_name, slice_value)
21✔
984
    }
985
}
30✔
986

987
#[cfg(test)]
988
mod tests {
989

990
    use super::*;
991

992
    #[test]
993
    fn test_http2_normalize_host() {
1✔
994
        let buf0 = "aBC.com:1234".as_bytes();
1✔
995
        let r0 = http2_normalize_host(buf0);
1✔
996
        assert_eq!(r0, "aBC.com".as_bytes().to_vec());
1✔
997
        let buf1 = "oisf.net".as_bytes();
1✔
998
        let r1 = http2_normalize_host(buf1);
1✔
999
        assert_eq!(r1, "oisf.net".as_bytes().to_vec());
1✔
1000
        let buf2 = "localhost:3000".as_bytes();
1✔
1001
        let r2 = http2_normalize_host(buf2);
1✔
1002
        assert_eq!(r2, "localhost".as_bytes().to_vec());
1✔
1003
        let buf3 = "user:pass@localhost".as_bytes();
1✔
1004
        let r3 = http2_normalize_host(buf3);
1✔
1005
        assert_eq!(r3, "localhost".as_bytes().to_vec());
1✔
1006
        let buf4 = "user:pass@localhost:123".as_bytes();
1✔
1007
        let r4 = http2_normalize_host(buf4);
1✔
1008
        assert_eq!(r4, "localhost".as_bytes().to_vec());
1✔
1009
    }
1✔
1010

1011
    #[test]
1012
    fn test_http2_header_trimspaces() {
1✔
1013
        let buf0 = "nospaces".as_bytes();
1✔
1014
        let r0 = http2_header_trimspaces(buf0);
1✔
1015
        assert_eq!(r0, "nospaces".as_bytes());
1✔
1016
        let buf1 = " spaces\t".as_bytes();
1✔
1017
        let r1 = http2_header_trimspaces(buf1);
1✔
1018
        assert_eq!(r1, "spaces".as_bytes());
1✔
1019
        let buf2 = " \t".as_bytes();
1✔
1020
        let r2 = http2_header_trimspaces(buf2);
1✔
1021
        assert_eq!(r2, "".as_bytes());
1✔
1022
    }
1✔
1023

1024
    #[test]
1025
    fn test_http2_frames_get_header_value() {
1✔
1026
        let mut tx = HTTP2Transaction::new();
1✔
1027
        let head = parser::HTTP2FrameHeader {
1✔
1028
            length: 0,
1✔
1029
            ftype: parser::HTTP2FrameType::Headers as u8,
1✔
1030
            flags: 0,
1✔
1031
            reserved: 0,
1✔
1032
            stream_id: 1,
1✔
1033
        };
1✔
1034
        let mut blocks = Vec::new();
1✔
1035
        let b = parser::HTTP2FrameHeaderBlock {
1✔
1036
            name: "Host".as_bytes().to_vec().into(),
1✔
1037
            value: "abc.com".as_bytes().to_vec().into(),
1✔
1038
            error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1✔
1039
            sizeupdate: 0,
1✔
1040
        };
1✔
1041
        blocks.push(b);
1✔
1042
        let b2 = parser::HTTP2FrameHeaderBlock {
1✔
1043
            name: "Host".as_bytes().to_vec().into(),
1✔
1044
            value: "efg.net".as_bytes().to_vec().into(),
1✔
1045
            error: parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess,
1✔
1046
            sizeupdate: 0,
1✔
1047
        };
1✔
1048
        blocks.push(b2);
1✔
1049
        let hs = parser::HTTP2FrameHeaders {
1✔
1050
            padlength: None,
1✔
1051
            priority: None,
1✔
1052
            blocks,
1✔
1053
        };
1✔
1054
        let txdata = HTTP2FrameTypeData::HEADERS(hs);
1✔
1055
        tx.frames_ts.push(HTTP2Frame {
1✔
1056
            header: head,
1✔
1057
            data: txdata,
1✔
1058
        });
1✔
1059
        match http2_frames_get_header_value(&mut tx, Direction::ToServer, "Host") {
1✔
1060
            Ok(x) => {
1✔
1061
                assert_eq!(x, "abc.com, efg.net".as_bytes());
1✔
1062
            }
1063
            Err(e) => {
1064
                panic!("Result should not have been an error: {:?}", e);
1065
            }
1066
        }
1067
    }
1✔
1068
}
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