Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit abc8e91

Browse files
committed
Merge branch 'brotli-fix' Fixes #4
2 parents cda8bae + 689fd77 commit abc8e91

File tree

2 files changed

+161
-111
lines changed

2 files changed

+161
-111
lines changed

src/HTTP2_Stream.cc

Lines changed: 87 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ using namespace analyzer::mitrecnd;
1010

1111
/**
1212
* HTTP2_Stream::UncompressedOutput : public analyzer::OutputHandler
13-
*
13+
*
1414
* Description: The output handler type used by the zip decompression api.
1515
*
1616
*/
@@ -43,10 +43,11 @@ HTTP2_HalfStream::HTTP2_HalfStream(HTTP2_Analyzer* analyzer, uint32_t stream_id,
4343
this->peerStreamEnded = false;
4444
this->zip = nullptr;
4545
this->send_size = true;
46-
4746
this->data_size = 0;
4847
this->contentLength = 0;
4948
this->contentEncodingId = DATA_ENCODING_IDENTITY;
49+
this->brotli = nullptr;
50+
this->brotli_buffer = nullptr;
5051
}
5152

5253
HTTP2_HalfStream::~HTTP2_HalfStream()
@@ -55,6 +56,14 @@ HTTP2_HalfStream::~HTTP2_HalfStream()
5556
zip->Done();
5657
delete zip;
5758
}
59+
60+
if (this->brotli != nullptr) {
61+
BrotliDecoderDestroyInstance(this->brotli);
62+
}
63+
64+
if (this->brotli_buffer != nullptr) {
65+
delete this->brotli_buffer;
66+
}
5867
}
5968

6069
bool HTTP2_HalfStream::processHeaders(uint8_t** headerBlockFragmentPtr, uint32_t& len,
@@ -146,14 +155,14 @@ void HTTP2_HalfStream::extractDataInfoHeaders(std::string& name, std::string& va
146155
}
147156

148157
void HTTP2_HalfStream::SubmitData(int len, const char* buf){
149-
158+
150159
// if partial data
151160
if ((this->send_size && this->contentLength > 0 && len < this->contentLength)
152161
|| !this->send_size) {
153162
file_mgr->DataIn(reinterpret_cast<const u_char*>(buf), len, this->dataOffset,
154163
this->analyzer->GetAnalyzerTag(), this->analyzer->Conn(),
155164
this->isOrig, this->precomputed_file_id);
156-
165+
157166
this->dataOffset += len;
158167
}
159168
else{
@@ -165,14 +174,14 @@ void HTTP2_HalfStream::SubmitData(int len, const char* buf){
165174

166175
void HTTP2_HalfStream::EndofData(void)
167176
{
168-
// If a unique file identifier has been created then use it, otherwise
177+
// If a unique file identifier has been created then use it, otherwise
169178
// relay to the file manager all information it needs to uniquely identify
170179
// the message.
171180
if (!this->precomputed_file_id.empty()) {
172181
file_mgr->EndOfFile(this->precomputed_file_id);
173182
} else {
174183
file_mgr->EndOfFile(this->analyzer->GetAnalyzerTag(),
175-
this->analyzer->Conn(),
184+
this->analyzer->Conn(),
176185
this->isOrig);
177186
}
178187
}
@@ -188,11 +197,15 @@ void HTTP2_HalfStream::DeliverBody(int len, const char* data, int trailing_CRLF)
188197
break;
189198
case DATA_ENCODING_BROTLI:
190199
if (this->dataBlockCnt == 1) { // Begin Entity
191-
this->brotli = BrotliDecoderCreateInstance(0, 0, 0);
200+
this->brotli = BrotliDecoderCreateInstance(0, 0, 0);
201+
this->brotli_buffer = new uint8_t[BROTLI_BUFFER_SIZE];
192202
}
193203
translateBrotliBody(len, data);
194204
if (trailing_CRLF) { // End Entity
205+
delete this->brotli_buffer;
206+
this->brotli_buffer = nullptr;
195207
BrotliDecoderDestroyInstance(this->brotli);
208+
this->brotli = nullptr;
196209
}
197210
break;
198211
case DATA_ENCODING_AES128GCM: // AES encrypted with 128 bit Key in Galois/Counter Mode
@@ -220,7 +233,7 @@ void HTTP2_HalfStream::translateZipBody(int len, const char* data, int method)
220233
{
221234
if (!zip){
222235
// We don't care about the direction here.
223-
zip = new zip::ZIP_Analyzer(this->analyzer->Conn(), false,
236+
zip = new zip::ZIP_Analyzer(this->analyzer->Conn(), false,
224237
(zip::ZIP_Analyzer::Method) method);
225238
zip->SetOutputHandler(new UncompressedOutput(this));
226239
}
@@ -230,21 +243,55 @@ void HTTP2_HalfStream::translateZipBody(int len, const char* data, int method)
230243
void HTTP2_HalfStream::translateBrotliBody(int len, const char* data)
231244
{
232245
BrotliDecoderResult result;
233-
size_t total_out = 0;
246+
bool repeat;
247+
size_t bytes_decompressed;
234248
size_t available_in = len;
235249
const uint8_t* next_in = (const uint8_t*) data;
236-
size_t available_out = MAX_FRAME_SIZE;
237-
uint8_t *next_out = this->brotli_buffer;
238250

239-
result = BrotliDecoderDecompressStream(this->brotli,
240-
&available_in,
241-
&next_in,
242-
&available_out,
243-
&next_out,
244-
&total_out);
245-
if (result == BROTLI_DECODER_RESULT_SUCCESS) {
246-
DeliverBodyClear((int)available_out, (const char *)this->brotli_buffer, false);
247-
}
251+
do {
252+
repeat = false;
253+
size_t available_out = BROTLI_BUFFER_SIZE;
254+
uint8_t *next_out = this->brotli_buffer;
255+
256+
result = BrotliDecoderDecompressStream(
257+
this->brotli, &available_in, &next_in,
258+
&available_out, &next_out, NULL);
259+
260+
bytes_decompressed = BROTLI_BUFFER_SIZE - available_out;
261+
262+
if (result == BROTLI_DECODER_RESULT_SUCCESS
263+
&& available_in > 0) {
264+
this->analyzer->Weird("Unexpected left-over bytes in brotli decompression");
265+
}
266+
267+
switch(result) {
268+
case BROTLI_DECODER_RESULT_ERROR:
269+
{
270+
BrotliDecoderErrorCode code = BrotliDecoderGetErrorCode(this->brotli);
271+
const char* error_string = BrotliDecoderErrorString(code);
272+
reporter->Error("Brotli decoder error: %s", error_string);
273+
break;
274+
}
275+
case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
276+
// Set repeat so this sequence continues until all output data
277+
// is extracted
278+
repeat = true;
279+
// Don't break -- let this fall through to the below case(s)
280+
case BROTLI_DECODER_RESULT_SUCCESS:
281+
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
282+
{
283+
if (bytes_decompressed > 0) {
284+
DeliverBodyClear((int)bytes_decompressed,
285+
(const char *)this->brotli_buffer, false);
286+
}
287+
break;
288+
}
289+
default:
290+
// Unexpected/undocumented result
291+
reporter->Warning("Brotli decoder returned unexpected result");
292+
break;
293+
}
294+
} while (repeat);
248295
}
249296

250297
void HTTP2_HalfStream::processData(HTTP2_Data_Frame* data)
@@ -292,10 +339,10 @@ void HTTP2_OrigStream::handleFrame(HTTP2_Frame* frame)
292339
this->Open_State(frame);
293340
break;
294341
case HTTP2_STREAM_STATE_HALF_CLOSED:
295-
this->Open_State(frame);
342+
this->Open_State(frame);
296343
break;
297344
case HTTP2_STREAM_STATE_CLOSED:
298-
this->Closed_State(frame);
345+
this->Closed_State(frame);
299346
break;
300347
default:
301348
break;
@@ -364,7 +411,7 @@ void HTTP2_OrigStream::ProcessHeaderBlock(HTTP2_Header_Frame_Base* header)
364411
if (name[0] == ':') {
365412
// Determine if this is one of the Pseudo Headers
366413
if (name == ":authority") {
367-
std::string token = value.substr(value.find("@") + 1, std::string::npos);
414+
std::string token = value.substr(value.find("@") + 1, std::string::npos);
368415
this->request_host = token.substr(0, token.find(":"));
369416
this->request_authority = value;
370417
}
@@ -392,9 +439,9 @@ void HTTP2_OrigStream::ProcessHeaderBlock(HTTP2_Header_Frame_Base* header)
392439
this->analyzer->HTTP2_Header(this->isOrig, this->id, name, value);
393440
}
394441

395-
// Retrieve the header info on a per header basis so that
396-
// persistent header storage is only necessary if http2_all_headers
397-
// is hooked.
442+
// Retrieve the header info on a per header basis so that
443+
// persistent header storage is only necessary if http2_all_headers
444+
// is hooked.
398445
extractDataInfoHeaders(name, value);
399446

400447
// Cache off if http2_all_headers is hooked.
@@ -448,12 +495,12 @@ void HTTP2_OrigStream::Idle_State(HTTP2_Frame* frame)
448495
if (http2_content_type) {
449496
if (this->contentType.empty()) {
450497
this->contentType = "text/plain";
451-
}
498+
}
452499
this->analyzer->HTTP2_ContentType(this->isOrig, this->id, this->contentType);
453500
}
454501

455502
// Advanced the state to 'open'
456-
this->state = HTTP2_STREAM_STATE_OPEN;
503+
this->state = HTTP2_STREAM_STATE_OPEN;
457504
} else { // expect continuation frames
458505

459506
}
@@ -463,7 +510,7 @@ void HTTP2_OrigStream::Idle_State(HTTP2_Frame* frame)
463510
// Advance the state and do some book-keeping
464511
this->state = HTTP2_STREAM_STATE_HALF_CLOSED;
465512
this->handleEndStream();
466-
}
513+
}
467514
break;
468515
}
469516
case NGHTTP2_DATA:
@@ -569,10 +616,10 @@ void HTTP2_RespStream::handleFrame(HTTP2_Frame* frame)
569616
this->Open_State(frame);
570617
break;
571618
case HTTP2_STREAM_STATE_HALF_CLOSED:
572-
this->Open_State(frame);
619+
this->Open_State(frame);
573620
break;
574621
case HTTP2_STREAM_STATE_CLOSED:
575-
this->Closed_State(frame);
622+
this->Closed_State(frame);
576623
break;
577624
default:
578625
break;
@@ -647,9 +694,9 @@ void HTTP2_RespStream::ProcessHeaderBlock(HTTP2_Header_Frame_Base* header)
647694
this->analyzer->HTTP2_Header(this->isOrig, this->id, name, value);
648695
}
649696

650-
// Retrieve the header info on a per header basis so that
651-
// persistent header storage is only necessary if http2_all_headers
652-
// is hooked.
697+
// Retrieve the header info on a per header basis so that
698+
// persistent header storage is only necessary if http2_all_headers
699+
// is hooked.
653700
extractDataInfoHeaders(name, value);
654701

655702
// Cache off if http2_all_headers is hooked.
@@ -693,15 +740,15 @@ void HTTP2_RespStream::Idle_State(HTTP2_Frame* frame)
693740
this->hlist.flushHeaders();
694741
}
695742

696-
this->state = HTTP2_STREAM_STATE_OPEN;
743+
this->state = HTTP2_STREAM_STATE_OPEN;
697744
} else { // expect continuation frames
698745

699746
}
700747

701748
if (header->isEndStream()){
702749
this->state = HTTP2_STREAM_STATE_HALF_CLOSED;
703750
this->handleEndStream();
704-
}
751+
}
705752
break;
706753
}
707754
case NGHTTP2_DATA:
@@ -781,7 +828,7 @@ void HTTP2_RespStream::Closed_State(HTTP2_Frame* frame)
781828
}
782829

783830
HTTP2_Stream::HTTP2_Stream(HTTP2_Analyzer* analyzer, uint32_t stream_id, nghttp2_hd_inflater* inflaters[2])
784-
{
831+
{
785832
this->id = stream_id;
786833
this->inflaters[0] = inflaters[0];
787834
this->inflaters[1] = inflaters[1];
@@ -826,9 +873,9 @@ bool HTTP2_Stream::handleFrame(HTTP2_Frame* f, bool orig) {
826873

827874
if (f->getType() == NGHTTP2_RST_STREAM){
828875
// TODO FIXME how to handle a rst stream frame
829-
// -- spec specifies rst frame sender must be able to accept frames
830-
// already in transit also priority frames can still be sent after a
831-
// reset can either keep stream allocated to allow for processing of
876+
// -- spec specifies rst frame sender must be able to accept frames
877+
// already in transit also priority frames can still be sent after a
878+
// reset can either keep stream allocated to allow for processing of
832879
// frames after reset or ignore further frames
833880
this->streamReset = true;
834881
this->streamResetter = orig;

0 commit comments

Comments
 (0)