Coverage Report

Created: 2026-02-26 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/connection_helper.cpp
Line
Count
Source
1
// Copyright 2025 Google LLC
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
////////////////////////////////////////////////////////////////////////////////
16
#include "connection_helper.h"
17
#include <cstring>
18
#include <unordered_set>
19
20
#include "mhd_action.h"
21
#include "http_post_enc.h"
22
#include "mempool_funcs.h"
23
#include "daemon_funcs.h"
24
#include "post_parser_funcs.h"
25
#include "response_funcs.h"
26
#include "stream_process_request.h"
27
#include "stream_funcs.h"
28
29
30
// MHD memory pool
31
struct mhd_MemoryPool *g_pool = nullptr;
32
const size_t g_pool_size = 14 * 1024;
33
std::string g_mpart_boundary;
34
35
// Body status
36
static std::unordered_set<const MHD_Connection*> g_post_parse_ready;
37
38
// Helper to clear memory pool
39
2
void destroy_global_pool() {
40
2
  if (g_pool) { mhd_pool_destroy(g_pool); g_pool = nullptr; }
41
2
}
42
43
44
// Helper to set body parsing ready
45
2.57k
void mark_post_parse_ready(MHD_Connection& c) {
46
2.57k
  g_post_parse_ready.insert(&c);
47
2.57k
}
48
49
// Helper to check parse body status
50
0
bool is_post_parse_ready(const MHD_Connection& c) {
51
0
  return g_post_parse_ready.find(&c) != g_post_parse_ready.end();
52
0
}
53
54
// Helper to clear parse body status
55
2.57k
void clear_post_parse_ready(const MHD_Connection& c) {
56
2.57k
  g_post_parse_ready.erase(&c);
57
2.57k
}
58
59
// Helper to destroy error response
60
5.40k
static bool destroy_error_response(MHD_Connection c) {
61
5.40k
  if (c.stage == mhd_HTTP_STAGE_START_REPLY) {
62
563
    MHD_response_destroy(c.rp.response);
63
563
    c.rp.response = nullptr;
64
563
    return true;
65
563
  }
66
67
4.84k
  return false;
68
5.40k
}
69
70
// Helper to randomly choose HTTP methods
71
1.33k
static std::string pick_method(FuzzedDataProvider& fdp) {
72
1.33k
  static const char* kMethods[] = {
73
1.33k
    "GET","POST","PUT","HEAD","DELETE","CONNECT","OPTIONS","TRACE","*"
74
1.33k
  };
75
1.33k
  return std::string(fdp.PickValueInArray(kMethods));
76
1.33k
}
77
78
// Helper to randomly choose http versions
79
1.33k
static std::string pick_http_version(FuzzedDataProvider& fdp) {
80
  // Common + a chance to be malformed to trigger version errors.
81
1.33k
  switch (fdp.ConsumeIntegralInRange<int>(0, 5)) {
82
680
    case 0: return "HTTP/1.1";
83
196
    case 1: return "HTTP/1.0";
84
131
    case 2: return "HTTP/2.0";
85
147
    case 3: return "HTTP/0.9";
86
125
    case 4: return "HTTX/1.1";
87
51
    default: {
88
51
      std::string s = "HTTP/";
89
51
      s.push_back(char('0' + fdp.ConsumeIntegralInRange<int>(0,9)));
90
51
      s.push_back('.');
91
51
      s.push_back(char('0' + fdp.ConsumeIntegralInRange<int>(0,9)));
92
51
      return s;
93
0
    }
94
1.33k
  }
95
1.33k
}
96
97
// Helper to check and expand buffer capcaity
98
1.03k
bool ensure_lbuf_capacity(MHD_Connection& c, size_t min_needed) {
99
1.03k
  if (!c.daemon) {
100
0
    return false;
101
0
  }
102
103
1.03k
  if (c.rq.cntn.lbuf.data && c.rq.cntn.lbuf.size >= min_needed) {
104
0
    return true;
105
0
  }
106
1.03k
  size_t have = c.rq.cntn.lbuf.size;
107
1.03k
  size_t need = (min_needed > have) ? (min_needed - have) : 0;
108
1.03k
  if (need) {
109
1.03k
    mhd_daemon_extend_lbuf_up_to(c.daemon, need, &c.rq.cntn.lbuf);
110
1.03k
  }
111
1.03k
  return (c.rq.cntn.lbuf.data != nullptr) && (c.rq.cntn.lbuf.size >= min_needed);
112
1.03k
}
113
114
// Dummy upload actions
115
const struct MHD_UploadAction kContinueAction = {
116
  mhd_UPLOAD_ACTION_CONTINUE, { nullptr }
117
};
118
const struct MHD_UploadAction kSuspend = {
119
  mhd_UPLOAD_ACTION_SUSPEND, { nullptr }
120
};
121
const struct MHD_UploadAction kAbort = {
122
  mhd_UPLOAD_ACTION_ABORT, { nullptr }
123
};
124
125
// Dummy reader function
126
const struct MHD_UploadAction *
127
dummy_reader(struct MHD_Request*, void*, const struct MHD_String*,
128
             const struct MHD_StringNullable*, const struct MHD_StringNullable*,
129
             const struct MHD_StringNullable*, size_t, const void*,
130
461
             uint_fast64_t, enum MHD_Bool) {
131
461
  return &kContinueAction;
132
461
}
133
134
// Dummy connection request ending function
135
const struct MHD_UploadAction *
136
2.57k
dummy_done(struct MHD_Request*, void*, enum MHD_PostParseResult) {
137
2.57k
  return &kContinueAction;
138
2.57k
}
139
140
// Dummy request callback function
141
static const struct MHD_Action*
142
dummy_request_cb(void* cls,
143
                 struct MHD_Request* request,
144
                 const struct MHD_String* path,
145
                 enum MHD_HTTP_Method method,
146
0
                 uint_fast64_t upload_size) {
147
0
  return nullptr;
148
0
}
149
150
void init_daemon_connection(FuzzedDataProvider& fdp,
151
2.85k
                                   MHD_Daemon& d, MHD_Connection& c) {
152
  // Basic initialisation
153
2.85k
  d = {};
154
2.85k
  c = {};
155
2.85k
  c.daemon = &d;
156
2.85k
  c.pool = g_pool;
157
158
  // Configure daemon memory pool
159
2.85k
  d.conns.cfg.mem_pool_size = g_pool_size;
160
2.85k
  d.conns.cfg.mem_pool_zeroing = MHD_MEMPOOL_ZEROING_ON_RESET;
161
162
  // Confiugre daemon request
163
2.85k
  d.req_cfg.large_buf.space_left = fdp.ConsumeIntegralInRange<size_t>(256, 65536);
164
2.85k
  d.req_cfg.strictness = static_cast<enum MHD_ProtocolStrictLevel>(
165
2.85k
      fdp.ConsumeIntegralInRange<int>(0, 2));
166
167
  // Safe guard for buffer space
168
2.85k
  if (fdp.ConsumeBool()) {
169
176
    const size_t clamp = fdp.ConsumeIntegralInRange<size_t>(64, 512);
170
176
    if (d.req_cfg.large_buf.space_left > clamp)
171
169
      d.req_cfg.large_buf.space_left = clamp;
172
176
  }
173
174
  // Configure connection request and general settings
175
2.85k
  c.discard_request = false;
176
2.85k
  c.suspended = false;
177
2.85k
  c.timeout.milsec = fdp.ConsumeIntegralInRange<uint32_t>(0, 4096);
178
2.85k
  c.timeout.last_act = 0;
179
180
  // Add dummy callback function
181
2.85k
  d.req_cfg.cb = dummy_request_cb;
182
2.85k
  d.req_cfg.cb_cls = nullptr;
183
2.85k
}
184
185
2.85k
void init_connection_buffer(FuzzedDataProvider& fdp, MHD_Connection& c) {
186
  // Prepare connection buffer in memory pool
187
2.85k
  size_t required = 0;
188
2.85k
  const size_t capacity = fdp.ConsumeIntegralInRange<size_t>(512, 16384);
189
2.85k
  char* buf = static_cast<char*>(mhd_pool_try_alloc(c.pool, capacity, &required));
190
2.85k
  if (!buf) {
191
1.52k
    c.read_buffer = nullptr;
192
1.52k
    c.read_buffer_size = 0;
193
1.52k
    c.read_buffer_offset = 0;
194
1.52k
    return;
195
1.52k
  }
196
197
  // Randomly choose configuration
198
1.33k
  std::string hdrs;
199
1.33k
  const std::string method = pick_method(fdp);
200
1.33k
  const std::string version = pick_http_version(fdp);
201
1.33k
  const std::string target  = (method == "*") ? "*" : (fdp.ConsumeBool() ? "/upload" : "/x?y=z");
202
203
  // Randomly break line endings in request line
204
1.33k
  const bool bare_lf = fdp.ConsumeBool();
205
1.33k
  const bool bare_cr = (!bare_lf) && fdp.ConsumeBool();
206
8.64k
  auto EOL = [&](bool final=false) {
207
8.64k
    if (bare_lf) {
208
5.74k
      return std::string("\n");
209
5.74k
    }
210
2.90k
    if (bare_cr) {
211
707
      return std::string("\r");
212
707
    }
213
214
2.19k
    return std::string("\r\n");
215
2.90k
  };
216
1.33k
  std::string req = method + " " + target + " " + version + EOL();
217
218
  // Host headers
219
1.33k
  int host_count = 0;
220
1.33k
  if (version == "HTTP/1.1") {
221
681
    host_count = fdp.ConsumeIntegralInRange<int>(0,2);
222
681
   } else {
223
649
    host_count = fdp.ConsumeIntegralInRange<int>(0,1);
224
649
   }
225
226
1.55k
  for (int i = 0; i < host_count; ++i) {
227
220
    if (fdp.ConsumeBool()) {
228
95
      hdrs += " Host: fuzz" + std::to_string(i) + EOL();
229
125
    } else if (fdp.ConsumeBool()) {
230
70
      hdrs += "Host : fuzz" + std::to_string(i) + EOL();
231
70
    } else {
232
55
      hdrs += "Host: fuzz" + std::to_string(i) + EOL();
233
55
    }
234
220
  }
235
236
  // Transfer-Encoding and Content-Length headers
237
1.33k
  const bool add_te = fdp.ConsumeBool();
238
1.33k
  const bool add_cl = fdp.ConsumeBool();
239
1.33k
  std::string te_val = fdp.PickValueInArray({"chunked","gzip","br","compress"});
240
1.33k
  if (add_te) {
241
415
    hdrs += "Transfer-Encoding: " + te_val + EOL();
242
415
  }
243
1.33k
  if (add_cl) {
244
179
    std::string cl;
245
179
    switch (fdp.ConsumeIntegralInRange<int>(0,3)) {
246
39
      case 0: cl = "0"; break;
247
72
      case 1: cl = std::to_string(fdp.ConsumeIntegralInRange<uint32_t>(0, 1<<20));
248
72
              break;
249
29
      case 2: cl = "18446744073709551616"; break;
250
39
      default: cl = "xyz"; break;
251
179
    }
252
179
    hdrs += "Content-Length: " + cl + EOL();
253
179
  }
254
255
  // Random minimum headers
256
1.33k
  if (fdp.ConsumeBool()) {
257
186
    hdrs += (fdp.ConsumeBool() ? "Expect: 100-continue" : "Expect: something-weird") + EOL();
258
186
  }
259
260
1.33k
  bool detect_mpart = false;
261
1.33k
  switch (c.rq.app_act.head_act.data.post_parse.enc) {
262
570
    case MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA:
263
570
      g_mpart_boundary = "fuzz" + std::to_string(fdp.ConsumeIntegral<uint32_t>());
264
570
      hdrs += "Content-Type: multipart/form-data; boundary=" + g_mpart_boundary + EOL();
265
570
      break;
266
49
    case MHD_HTTP_POST_ENCODING_FORM_URLENCODED:
267
49
      hdrs += "Content-Type: application/x-www-form-urlencoded" + EOL();
268
49
      break;
269
560
    case MHD_HTTP_POST_ENCODING_TEXT_PLAIN:
270
560
      hdrs += "Content-Type: text/plain" + EOL();
271
560
      break;
272
151
    default: case MHD_HTTP_POST_ENCODING_OTHER:
273
151
      detect_mpart = fdp.ConsumeBool();
274
151
      if (detect_mpart) {
275
58
        g_mpart_boundary = "fuzz" + std::to_string(fdp.ConsumeIntegral<uint32_t>());
276
58
        hdrs += "Content-Type: multipart/form-data; boundary=" + g_mpart_boundary + EOL();
277
93
      } else {
278
93
        hdrs += std::string("Content-Type: ")
279
93
             + (fdp.ConsumeBool() ? "application/x-www-form-urlencoded" : "text/plain") + EOL();
280
93
      }
281
151
      break;
282
1.33k
  }
283
284
  // Randomly add some chunked headers
285
1.33k
  const bool add_te_chunked = fdp.ConsumeBool();
286
1.33k
  if (add_te_chunked) {
287
302
    hdrs += "Transfer-Encoding: chunked" + EOL();
288
302
  }
289
1.33k
  if (fdp.ConsumeBool()) {
290
243
    const uint32_t cl = fdp.ConsumeIntegralInRange<uint32_t>(0, 256);
291
243
    hdrs += "Content-Length: " + std::to_string(cl) + EOL();
292
243
  }
293
1.33k
  if (add_te_chunked && fdp.ConsumeBool()) {
294
198
    if (fdp.ConsumeBool())
295
105
      hdrs += "Trailer: X-Fuzz-Trace" + EOL();
296
93
    else
297
93
      hdrs += "Trailer: X-One, X-Two" + EOL();
298
198
  }
299
1.33k
  if (fdp.ConsumeBool()) {
300
255
    hdrs += (fdp.ConsumeBool() ? "Expect: 100-continue" : "Expect: something-weird") + EOL();
301
255
  }
302
303
  // Connection ending line
304
1.33k
  hdrs += "Connection: close" + EOL();
305
1.33k
  std::string end = EOL() + EOL();
306
307
  // Write into the read buffer
308
1.33k
  std::string full = req + hdrs + end;
309
1.33k
  const size_t n = (full.size() <= capacity) ? full.size() : capacity;
310
1.33k
  memcpy(buf, full.data(), n);
311
312
1.33k
  c.read_buffer = buf;
313
1.33k
  c.read_buffer_size = capacity;
314
1.33k
  c.read_buffer_offset = n;
315
1.33k
}
316
317
2.85k
void init_parsing_configuration(FuzzedDataProvider& fdp, MHD_Connection& c) {
318
2.85k
  MHD_HTTP_PostEncoding enc;
319
320
  // Configure connection encoding abd methods
321
2.85k
  if (fdp.ConsumeBool()) {
322
1.78k
    c.rq.app_act.head_act.act = mhd_ACTION_POST_PARSE;
323
1.78k
  } else {
324
1.07k
    c.rq.app_act.head_act.act = mhd_ACTION_NO_ACTION;
325
1.07k
  }
326
2.85k
  if (fdp.ConsumeBool()) {
327
1.10k
    enc = MHD_HTTP_POST_ENCODING_TEXT_PLAIN;
328
1.75k
  } else if (fdp.ConsumeBool()) {
329
305
    enc = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
330
1.45k
  } else if (fdp.ConsumeBool()) {
331
1.28k
    enc = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
332
1.28k
  } else {
333
167
    enc = MHD_HTTP_POST_ENCODING_OTHER;
334
167
  }
335
336
2.85k
  c.rq.app_act.head_act.data.post_parse.buffer_size =
337
2.85k
    fdp.ConsumeIntegralInRange<size_t>(1, 4096);
338
2.85k
  c.rq.app_act.head_act.data.post_parse.max_nonstream_size =
339
2.85k
    fdp.ConsumeIntegralInRange<size_t>(1, 4096);
340
341
  // Confiugre head action for connection post parsing process
342
2.85k
  c.rq.app_act.head_act.data.post_parse.enc = enc;
343
2.85k
  c.rq.app_act.head_act.data.post_parse.stream_reader = dummy_reader;
344
2.85k
  c.rq.app_act.head_act.data.post_parse.reader_cls = nullptr;
345
2.85k
  c.rq.app_act.head_act.data.post_parse.done_cb = dummy_done;
346
2.85k
  c.rq.app_act.head_act.data.post_parse.done_cb_cls = nullptr;
347
2.85k
}
348
349
2.85k
void prepare_headers_and_parse(MHD_Connection& connection, size_t size) {
350
  // Manually add a parameter for parsing
351
5.32k
  auto add_hdr = [&](const char* name_c, const std::string& val_s) {
352
5.32k
    const size_t nlen = strlen(name_c);
353
5.32k
    const size_t vlen = val_s.size();
354
5.32k
    char* nbuf = static_cast<char*>(mhd_stream_alloc_memory(&connection, nlen + 1));
355
5.32k
    char* vbuf = static_cast<char*>(mhd_stream_alloc_memory(&connection, vlen + 1));
356
5.32k
    if (!nbuf || !vbuf) {
357
53
      return;
358
53
    }
359
5.27k
    memcpy(nbuf, name_c, nlen); nbuf[nlen] = '\0';
360
5.27k
    memcpy(vbuf, val_s.data(), vlen); vbuf[vlen] = '\0';
361
5.27k
    struct MHD_String name;
362
5.27k
    name.len  = nlen;
363
5.27k
    name.cstr = nbuf;
364
5.27k
    struct MHD_String value;
365
5.27k
    value.len  = vlen;
366
5.27k
    value.cstr = vbuf;
367
5.27k
    mhd_stream_add_field(&connection.h1_stream, MHD_VK_HEADER, &name, &value);
368
5.27k
  };
369
2.85k
  add_hdr("Host", "fuzz");
370
2.85k
  if ((size & 3u) == 0u) {
371
799
    add_hdr("Transfer-Encoding", "chunked");
372
799
  }
373
2.85k
  if ((size & 7u) == 0u) {
374
333
    add_hdr("Content-Length", "0");
375
333
  }
376
377
2.85k
  bool force_mpart = (connection.rq.app_act.head_act.data.post_parse.enc
378
2.85k
                        == MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA);
379
2.85k
  if (!force_mpart) {
380
1.57k
    force_mpart = ((size & 0x3Fu) == 0u);
381
1.57k
  }
382
2.85k
  if (force_mpart) {
383
1.33k
    if (g_mpart_boundary.empty()) {
384
0
      g_mpart_boundary = "fuzz" + std::to_string(size ^ 0x9e3779b97f4a7c15ULL);
385
0
    }
386
1.33k
    std::string ct = "multipart/form-data; boundary=" + g_mpart_boundary;
387
1.33k
    add_hdr("Content-Type", ct);
388
1.33k
  }
389
390
  // If we wrote a real HTTP header for multipart, parse it so Content-Type is visible
391
2.85k
  connection.stage = mhd_HTTP_STAGE_INIT;
392
2.85k
  bool got_line = mhd_stream_get_request_line(&connection);
393
2.85k
  if (destroy_error_response(connection)) {
394
314
    return;
395
314
  }
396
2.54k
  if (got_line && connection.stage == mhd_HTTP_STAGE_REQ_LINE_RECEIVED) {
397
531
    mhd_stream_switch_to_rq_headers_proc(&connection);
398
531
  }
399
2.54k
  mhd_stream_parse_request_headers(&connection);
400
401
  // Only proceed to post-parse if header parsing did not bail out
402
2.54k
  destroy_error_response(connection);
403
2.54k
}
404
405
2.57k
void prepare_body_and_process(MHD_Connection& connection, std::string& body, size_t body_size, bool use_stream_body) {
406
  // Use streaming if boundary is not empty
407
2.57k
  if (!g_mpart_boundary.empty()) {
408
2.57k
    std::string wrapped;
409
2.57k
    wrapped.reserve(body.size() + g_mpart_boundary.size() * 2 + 128);
410
2.57k
    wrapped += "\r\n--"; wrapped += g_mpart_boundary; wrapped += "\r\n";
411
2.57k
    wrapped += "Content-Disposition: form-data; name=\"x\"; filename=\"f\"\r\n";
412
2.57k
    wrapped += "Content-Type: application/octet-stream\r\n\r\n";
413
2.57k
    wrapped += body; wrapped += "\r\n";
414
2.57k
    wrapped += "--"; wrapped += g_mpart_boundary; wrapped += "--\r\n";
415
2.57k
    body.swap(wrapped);
416
2.57k
    body_size = body.size();
417
2.57k
  }
418
419
2.57k
  if (!use_stream_body) {
420
    // Fuzz mhd_stream_prepare_for_post_parse once and mhd_stream_post_parse
421
1.35k
    mhd_stream_prepare_for_post_parse(&connection);
422
1.35k
    mhd_stream_post_parse(&connection, &body_size, &body[0]);
423
1.35k
    mark_post_parse_ready(connection);
424
1.35k
  } else {
425
    // Try prepare the body by streaming connection buffer
426
1.22k
    const bool want_chunked = (connection.rq.have_chunked_upload == MHD_YES);
427
428
1.22k
    std::string to_feed;
429
1.22k
    if (want_chunked) {
430
730
      auto append_chunk = [&](const char* data, size_t len) {
431
730
        char hex[32];
432
730
        const int n = snprintf(hex, sizeof(hex), "%zx", len);
433
730
        to_feed.append(hex, (size_t)n);
434
730
        if ((len & 3u) == 0u) {
435
230
          to_feed += ";ext=1";
436
230
        }
437
730
        to_feed += "\r\n";
438
730
        to_feed.append(data, len);
439
730
        to_feed += "\r\n";
440
730
      };
441
365
      if (body_size <= 32) {
442
0
        append_chunk(body.data(), body_size);
443
365
      } else {
444
365
        const size_t mid = body_size / 2;
445
365
        append_chunk(body.data(), mid);
446
365
        append_chunk(body.data() + mid, body_size - mid);
447
365
      }
448
365
      to_feed += "0\r\n";
449
365
      if (body_size & 1) {
450
141
        to_feed += "X-Fuzz-Trace: 1\r\n\r\n";
451
224
      } else {
452
224
        to_feed += "\r\n";
453
224
      }
454
857
    } else {
455
      // Non-chunked body is handled as is
456
857
      to_feed.assign(body.data(), body_size);
457
857
    }
458
459
    // Stage into the connection read buffer (allocate if needed).
460
1.22k
    size_t feed_sz = to_feed.size();
461
1.22k
    bool staged = false;
462
1.22k
    if (connection.read_buffer && connection.read_buffer_size >= feed_sz) {
463
170
      memcpy(connection.read_buffer, to_feed.data(), feed_sz);
464
170
      connection.read_buffer_offset = feed_sz;
465
170
      staged = true;
466
1.05k
    } else {
467
1.05k
      size_t need = 0;
468
1.05k
      char *nb = (char*) mhd_pool_try_alloc(connection.pool, feed_sz, &need);
469
1.05k
      if (nb) {
470
862
        memcpy(nb, to_feed.data(), feed_sz);
471
862
        connection.read_buffer = nb;
472
862
        connection.read_buffer_size = feed_sz;
473
862
        connection.read_buffer_offset = feed_sz;
474
862
        staged = true;
475
862
      }
476
1.05k
    }
477
478
1.22k
    if (staged) {
479
      // Use stream body approach if success
480
1.03k
      const size_t min_needed = body_size + 1;
481
1.03k
      if (ensure_lbuf_capacity(connection, min_needed)) {
482
        // Only post parse the request if buffer is enough
483
907
        connection.stage = mhd_HTTP_STAGE_BODY_RECEIVING;
484
907
        connection.rq.have_chunked_upload = MHD_NO;
485
907
        connection.rq.cntn.cntn_size = (uint64_t) body_size;
486
907
        mhd_stream_prepare_for_post_parse(&connection);
487
907
        mhd_stream_process_request_body(&connection);
488
907
        mark_post_parse_ready(connection);
489
907
      } else {
490
        // Fall back if not enough buffer
491
125
        size_t tmp = body_size;
492
125
        mhd_stream_prepare_for_post_parse(&connection);
493
125
        mhd_stream_post_parse(&connection, &tmp, body.data());
494
125
        mark_post_parse_ready(connection);
495
125
      }
496
1.03k
    } else {
497
      // Use post prase approach if stream body failed
498
190
      mhd_stream_prepare_for_post_parse(&connection);
499
190
      mhd_stream_post_parse(&connection, &body_size, &body[0]);
500
190
      mark_post_parse_ready(connection);
501
190
    }
502
1.22k
  }
503
2.57k
}
504
505
2.57k
void final_cleanup(MHD_Connection& connection, MHD_Daemon& daemon) {
506
  // Post process response
507
2.57k
  mhd_stream_switch_from_recv_to_send(&connection);
508
2.57k
  mhd_stream_process_req_recv_finished(&connection);
509
2.57k
  mhd_stream_release_write_buffer(&connection);
510
511
  // Release buffers in daemon to avoid memory leakage
512
2.57k
  if (connection.rq.cntn.lbuf.data != nullptr || connection.rq.cntn.lbuf.size != 0) {
513
0
    mhd_daemon_free_lbuf(&daemon, &connection.rq.cntn.lbuf);
514
0
  }
515
516
  // Clean post parse body status
517
2.57k
  clear_post_parse_ready(connection);
518
2.57k
}