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