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

OISF / suricata / 23350122333

20 Mar 2026 03:33PM UTC coverage: 76.492% (-2.8%) from 79.315%
23350122333

Pull #15053

github

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

113 of 129 new or added lines in 9 files covered. (87.6%)

9534 existing lines in 453 files now uncovered.

256601 of 335461 relevant lines covered (76.49%)

4680806.66 hits per line

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

83.45
/rust/src/http2/decompression.rs
1
/* Copyright (C) 2021 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 crate::direction::Direction;
19
use brotli;
20
use flate2::read::{DeflateDecoder, GzDecoder};
21
use std;
22
use std::io;
23
use std::io::{Cursor, Read, Write};
24

25
pub const HTTP2_DECOMPRESSION_CHUNK_SIZE: usize = 0x1000; // 4096
26

27
#[repr(u8)]
28
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
29
pub enum HTTP2ContentEncoding {
30
    Unknown = 0,
31
    Gzip = 1,
32
    Br = 2,
33
    Deflate = 3,
34
    Unrecognized = 4,
35
}
36

37
//a cursor turning EOF into blocking errors
38
#[derive(Debug)]
39
pub struct HTTP2cursor {
40
    pub cursor: Cursor<Vec<u8>>,
41
}
42

43
impl HTTP2cursor {
44
    pub fn new() -> HTTP2cursor {
23✔
45
        HTTP2cursor {
23✔
46
            cursor: Cursor::new(Vec::new()),
23✔
47
        }
23✔
48
    }
23✔
49

50
    pub fn set_position(&mut self, pos: u64) {
46✔
51
        return self.cursor.set_position(pos);
46✔
52
    }
46✔
53

54
    pub fn clear(&mut self) {
46✔
55
        self.cursor.get_mut().clear();
46✔
56
        self.cursor.set_position(0);
46✔
57
    }
46✔
58
}
59

60
// we need to implement this as flate2 and brotli crates
61
// will read from this object
62
impl Read for HTTP2cursor {
63
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
54✔
64
        //use the cursor, except it turns eof into blocking error
54✔
65
        let r = self.cursor.read(buf);
54✔
66
        match r {
54✔
67
            Err(ref err) => {
×
68
                if err.kind() == io::ErrorKind::UnexpectedEof {
×
69
                    return Err(io::ErrorKind::WouldBlock.into());
×
70
                }
×
71
            }
72
            Ok(0) => {
73
                //regular EOF turned into blocking error
74
                return Err(io::ErrorKind::WouldBlock.into());
25✔
75
            }
76
            Ok(_n) => {}
29✔
77
        }
78
        return r;
29✔
79
    }
54✔
80
}
81

82
pub enum HTTP2Decompresser {
83
    Unassigned,
84
    // Box because large.
85
    Gzip(Box<GzDecoder<HTTP2cursor>>),
86
    // Box because large.
87
    Brotli(Box<brotli::Decompressor<HTTP2cursor>>),
88
    // This one is not so large, at 88 bytes as of doing this, but box
89
    // for consistency.
90
    Deflate(Box<DeflateDecoder<HTTP2cursor>>),
91
}
92

93
impl std::fmt::Debug for HTTP2Decompresser {
94
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
95
        match self {
×
96
            HTTP2Decompresser::Unassigned => write!(f, "UNASSIGNED"),
×
97
            HTTP2Decompresser::Gzip(_) => write!(f, "GZIP"),
×
98
            HTTP2Decompresser::Brotli(_) => write!(f, "BROTLI"),
×
99
            HTTP2Decompresser::Deflate(_) => write!(f, "DEFLATE"),
×
100
        }
101
    }
×
102
}
103

104
#[derive(Debug)]
105
struct HTTP2DecoderHalf {
106
    encoding: HTTP2ContentEncoding,
107
    decoder: HTTP2Decompresser,
108
}
109

110
pub trait GetMutCursor {
111
    fn get_mut(&mut self) -> &mut HTTP2cursor;
112
}
113

114
impl GetMutCursor for GzDecoder<HTTP2cursor> {
115
    fn get_mut(&mut self) -> &mut HTTP2cursor {
102✔
116
        return self.get_mut();
102✔
117
    }
102✔
118
}
119

120
impl GetMutCursor for DeflateDecoder<HTTP2cursor> {
121
    fn get_mut(&mut self) -> &mut HTTP2cursor {
6✔
122
        return self.get_mut();
6✔
123
    }
6✔
124
}
125

126
impl GetMutCursor for brotli::Decompressor<HTTP2cursor> {
127
    fn get_mut(&mut self) -> &mut HTTP2cursor {
30✔
128
        return self.get_mut();
30✔
129
    }
30✔
130
}
131

132
fn http2_decompress<'a>(
46✔
133
    decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>,
46✔
134
) -> io::Result<&'a [u8]> {
46✔
135
    match decoder.get_mut().cursor.write_all(input) {
46✔
136
        Ok(()) => {}
46✔
137
        Err(e) => {
×
138
            return Err(e);
×
139
        }
140
    }
141
    let mut offset = 0;
46✔
142
    decoder.get_mut().set_position(0);
46✔
143
    output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0);
46✔
144
    loop {
145
        match decoder.read(&mut output[offset..]) {
85✔
146
            Ok(0) => {
147
                break;
38✔
148
            }
149
            Ok(n) => {
39✔
150
                offset += n;
39✔
151
                if offset == output.len() {
39✔
152
                    output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0);
14✔
153
                }
25✔
154
            }
155
            Err(e) => {
8✔
156
                if e.kind() == io::ErrorKind::WouldBlock {
8✔
157
                    break;
8✔
UNCOV
158
                }
×
UNCOV
159
                return Err(e);
×
160
            }
161
        }
162
    }
163
    //brotli does not consume all input if it reaches some end
164
    decoder.get_mut().clear();
46✔
165
    return Ok(&output[..offset]);
46✔
166
}
46✔
167

168
impl HTTP2DecoderHalf {
169
    pub fn new() -> HTTP2DecoderHalf {
1,040✔
170
        HTTP2DecoderHalf {
1,040✔
171
            encoding: HTTP2ContentEncoding::Unknown,
1,040✔
172
            decoder: HTTP2Decompresser::Unassigned,
1,040✔
173
        }
1,040✔
174
    }
1,040✔
175

176
    pub fn http2_encoding_fromvec(&mut self, input: &[u8]) {
23✔
177
        //use first encoding...
23✔
178
        if self.encoding == HTTP2ContentEncoding::Unknown {
23✔
179
            if input == b"gzip" {
23✔
180
                self.encoding = HTTP2ContentEncoding::Gzip;
17✔
181
                self.decoder =
17✔
182
                    HTTP2Decompresser::Gzip(Box::new(GzDecoder::new(HTTP2cursor::new())));
17✔
183
            } else if input == b"deflate" {
17✔
184
                self.encoding = HTTP2ContentEncoding::Deflate;
2✔
185
                self.decoder =
2✔
186
                    HTTP2Decompresser::Deflate(Box::new(DeflateDecoder::new(HTTP2cursor::new())));
2✔
187
            } else if input == b"br" {
4✔
188
                self.encoding = HTTP2ContentEncoding::Br;
4✔
189
                self.decoder = HTTP2Decompresser::Brotli(Box::new(brotli::Decompressor::new(
4✔
190
                    HTTP2cursor::new(),
4✔
191
                    HTTP2_DECOMPRESSION_CHUNK_SIZE,
4✔
192
                )));
4✔
193
            } else {
4✔
UNCOV
194
                self.encoding = HTTP2ContentEncoding::Unrecognized;
×
UNCOV
195
            }
×
UNCOV
196
        }
×
197
    }
23✔
198

199
    pub fn decompress<'a>(
1,136✔
200
        &mut self, input: &'a [u8], output: &'a mut Vec<u8>,
1,136✔
201
    ) -> io::Result<&'a [u8]> {
1,136✔
202
        match self.decoder {
1,136✔
203
            HTTP2Decompresser::Gzip(ref mut gzip_decoder) => {
34✔
204
                let r = http2_decompress(&mut *gzip_decoder.as_mut(), input, output);
34✔
205
                if r.is_err() {
34✔
UNCOV
206
                    self.decoder = HTTP2Decompresser::Unassigned;
×
207
                }
34✔
208
                return r;
34✔
209
            }
210
            HTTP2Decompresser::Brotli(ref mut br_decoder) => {
10✔
211
                let r = http2_decompress(&mut *br_decoder.as_mut(), input, output);
10✔
212
                if r.is_err() {
10✔
UNCOV
213
                    self.decoder = HTTP2Decompresser::Unassigned;
×
214
                }
10✔
215
                return r;
10✔
216
            }
217
            HTTP2Decompresser::Deflate(ref mut df_decoder) => {
2✔
218
                let r = http2_decompress(&mut *df_decoder.as_mut(), input, output);
2✔
219
                if r.is_err() {
2✔
UNCOV
220
                    self.decoder = HTTP2Decompresser::Unassigned;
×
221
                }
2✔
222
                return r;
2✔
223
            }
224
            _ => {}
1,090✔
225
        }
1,090✔
226
        return Ok(input);
1,090✔
227
    }
1,136✔
228
}
229

230
#[derive(Debug)]
231
pub struct HTTP2Decoder {
232
    decoder_tc: HTTP2DecoderHalf,
233
    decoder_ts: HTTP2DecoderHalf,
234
}
235

236
impl HTTP2Decoder {
237
    pub fn new() -> HTTP2Decoder {
520✔
238
        HTTP2Decoder {
520✔
239
            decoder_tc: HTTP2DecoderHalf::new(),
520✔
240
            decoder_ts: HTTP2DecoderHalf::new(),
520✔
241
        }
520✔
242
    }
520✔
243

244
    pub fn http2_encoding_fromvec(&mut self, input: &[u8], dir: Direction) {
23✔
245
        if dir == Direction::ToClient {
23✔
246
            self.decoder_tc.http2_encoding_fromvec(input);
23✔
247
        } else {
23✔
UNCOV
248
            self.decoder_ts.http2_encoding_fromvec(input);
×
UNCOV
249
        }
×
250
    }
23✔
251

252
    pub fn decompress<'a>(
1,136✔
253
        &mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction,
1,136✔
254
    ) -> io::Result<&'a [u8]> {
1,136✔
255
        if dir == Direction::ToClient {
1,136✔
256
            return self.decoder_tc.decompress(input, output);
1,030✔
257
        } else {
258
            return self.decoder_ts.decompress(input, output);
106✔
259
        }
260
    }
1,136✔
261
}
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