Coverage Report

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