6565#include " httpserver/detail/http_endpoint.hpp"
6666#include " httpserver/detail/lambda_resource.hpp"
6767#include " httpserver/detail/modded_request.hpp"
68+ #include " httpserver/detail/upload_pipeline.hpp"
6869#include " httpserver/http_request.hpp"
6970#include " httpserver/http_resource.hpp"
7071#include " httpserver/http_response.hpp"
@@ -362,94 +363,14 @@ size_t webserver_impl::unescaper_func(void * cls, struct MHD_Connection *c, char
362363 return std::char_traits<char >::length (s);
363364}
364365
365- MHD_Result webserver_impl::handle_post_form_arg (detail::modded_request* mr,
366- const char * key, const char * data, size_t size, uint64_t off) {
367- // MHD may invoke the post iterator with a null key on a continuation
368- // chunk (off > 0): the field name was supplied on the first call and
369- // is not repeated. With no field name there is nothing to store the
370- // value under, so silently accept the chunk (MHD_YES tells MHD to
371- // continue; MHD_NO would abort the whole request). Guarding here also
372- // stops the raw pointer from reaching std::string, which throws
373- // std::logic_error on null and aborts the process via std::terminate
374- // because the throw escapes a C callback. See issue #375 (same class
375- // of bug as the null-uri fix in uri_log, issue #371).
376- if (key == nullptr ) {
377- return MHD_YES ;
378- }
379- // No file: set the arg key/value and return. A non-zero @p off
380- // means MHD is feeding us a continuation chunk of a previously-
381- // started value, so append rather than replace.
382- if (off > 0 ) {
383- mr->request ->grow_last_arg (key, std::string (data, size));
384- } else {
385- mr->request ->set_arg (key, std::string (data, size));
386- }
387- return MHD_YES ;
388- }
389-
390- bool webserver_impl::setup_new_upload_file_info (http::file_info& file,
391- const char * filename, const char * content_type,
392- const char * transfer_encoding) const {
393- // First chunk for this (key, filename) pair: choose the on-disk
394- // destination path (random if generate_random_filename_on_upload,
395- // otherwise sanitize the client-supplied filename) and prime the
396- // file_info with content_type / transfer_encoding when MHD gave
397- // them to us.
398- if (parent->config .generate_random_filename_on_upload ) {
399- file.set_file_system_file_name (
400- http_utils::generate_random_upload_filename (parent->config .file_upload_dir ));
401- } else {
402- std::string safe_name = http_utils::sanitize_upload_filename (filename);
403- if (safe_name.empty ()) return false ;
404- file.set_file_system_file_name (parent->config .file_upload_dir + " /" + safe_name);
405- }
406- // Avoid appending to a leftover file from a previous request.
407- unlink (file.get_file_system_file_name ().c_str ());
408- if (content_type != nullptr ) file.set_content_type (content_type);
409- if (transfer_encoding != nullptr ) file.set_transfer_encoding (transfer_encoding);
410- return true ;
411- }
412-
413- void webserver_impl::manage_upload_stream (detail::modded_request* mr,
414- const char * filename, const char * key, http::file_info& file) {
415- // If MHD switches us to a different (filename, key) pair, close the
416- // previous output stream. The four-way OR covers fresh state (both
417- // tracking strings empty) and either coordinate changing.
418- if (mr->upload_filename .empty ()
419- || mr->upload_key .empty ()
420- || strcmp (filename, mr->upload_filename .c_str ()) != 0
421- || strcmp (key, mr->upload_key .c_str ()) != 0 ) {
422- if (mr->upload_ostrm != nullptr ) mr->upload_ostrm ->close ();
423- }
424- // Open a stream when we don't already have one (first chunk, or
425- // just-closed above).
426- if (mr->upload_ostrm == nullptr || !mr->upload_ostrm ->is_open ()) {
427- mr->upload_key = key;
428- mr->upload_filename = filename;
429- mr->upload_ostrm = std::make_unique<std::ofstream>();
430- mr->upload_ostrm ->open (file.get_file_system_file_name (),
431- std::ios::binary | std::ios::app);
432- }
433- }
434-
435- MHD_Result webserver_impl::process_file_upload (detail::modded_request* mr,
436- const char * key, const char * filename, const char * content_type,
437- const char * transfer_encoding, const char * data, size_t size) const {
438- http::file_info& file = mr->request ->get_or_create_file_info (key, filename);
439- if (file.get_file_system_file_name ().empty ()) {
440- if (!setup_new_upload_file_info (file, filename, content_type, transfer_encoding)) {
441- return MHD_NO ;
442- }
443- }
444- manage_upload_stream (mr, filename, key, file);
445- if (size > 0 ) {
446- mr->upload_ostrm ->write (data, size);
447- if (!mr->upload_ostrm ->good ()) return MHD_NO ;
448- }
449- file.grow_file_size (size);
450- return MHD_YES ;
451- }
452-
366+ // MHD post-iterator trampoline. Registered with MHD_create_post_processor
367+ // (webserver_body_pipeline.cpp); its address is taken, so it stays a static
368+ // webserver_impl member. The upload logic moved to the upload_pipeline
369+ // behavior service (DR-014 §4.11). The no-file form-arg branch uses the
370+ // static upload_pipeline::handle_post_form_arg so it is reachable without an
371+ // owning webserver (post_iterator_null_key_test feeds a null-ws
372+ // modded_request); the file branch routes through the owning webserver's
373+ // upload_ instance.
453374MHD_Result webserver_impl::post_iterator (void *cls, enum MHD_ValueKind kind,
454375 const char *key, const char *filename, const char *content_type,
455376 const char *transfer_encoding, const char *data, uint64_t off, size_t size) {
@@ -458,23 +379,10 @@ MHD_Result webserver_impl::post_iterator(void *cls, enum MHD_ValueKind kind,
458379 auto * mr = static_cast <detail::modded_request*>(cls);
459380
460381 if (!filename) {
461- return handle_post_form_arg (mr, key, data, size, off);
462- }
463-
464- try {
465- if (mr->ws ->config .file_upload_target != FILE_UPLOAD_DISK_ONLY ) {
466- mr->request ->set_arg_flat (key,
467- std::string (mr->request ->get_arg (key)) + std::string (data, size));
468- }
469- if (*filename != ' \0 ' && mr->ws ->config .file_upload_target != FILE_UPLOAD_MEMORY_ONLY ) {
470- MHD_Result r = mr->ws ->impl_ ->process_file_upload (
471- mr, key, filename, content_type, transfer_encoding, data, size);
472- if (r != MHD_YES ) return r;
473- }
474- return MHD_YES ;
475- } catch (const http::generateFilenameException&) {
476- return MHD_NO ;
382+ return upload_pipeline::handle_post_form_arg (mr, key, data, size, off);
477383 }
384+ return mr->ws ->impl_ ->upload_ .iterate_file (
385+ mr, key, filename, content_type, transfer_encoding, data, size);
478386}
479387
480388} // namespace detail
0 commit comments