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