Unable to upload a file with Crow C++ due to segmentation fault error

353 Views Asked by At

I'm currently working on a C++ web server using Crow framework. I would like to be able to send a file from a form to my server. The problem that I'm facing is a segmentation fault (core dump) when I submit the form. It seems this is the line causing the problem : crow::multipart::message msg(req);

I don't know why since the request object (req) is not null. It cannot be because I'm testing the content-type before running the previous line. Help please. Here is the code of my HTML page.

<form class="p-5" name="submitform" id="submitform">
    <div class="form-group mb-3 row">
        <label for="name" class="col-md-2 col-form-label">Name of the task</label>
        <div class="col-md-10">
            <input type="text" class="form-control" id="name" name="name">
        </div>
    </div>
    <div class="form-group mb-3 row">
        <label for="executable" class="col-md-2 col-form-label">Executable file</label>
        <div class="col-md-10">
            <input type="file" class="form-control" id="executable" name="executable">
        </div>
    </div>
    <div class="form-group mb-3 row">
        <label for="data" class="col-md-2 col-form-label">Data file</label>
        <div class="col-md-10">
            <input type="file" class="form-control" id="data" name="data">
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
</form>

Javascript

<script>
    // Get the form element by its id
    var form = document.getElementById("submitform");
    // Add an event listener for the submit event
    form.addEventListener("submit", function(event) {
        // Prevent the default action of the form submission
        event.preventDefault();
        // Create a new FormData object to store the form data
        var formData = new FormData(form);
        // Create a new XMLHttpRequest object to send the request to the server
        var xhr = new XMLHttpRequest();
        // Open the request with the POST method and the /client-mode/tasks/new URL
        xhr.open("POST", "/client-mode/tasks/new");
        // Set the request header for the content type as multipart/form-data
        xhr.setRequestHeader("Content-Type", "multipart/form-data");
        // Add an event listener for the load event, which occurs when the response is received from the server
        xhr.addEventListener("load", function() {
            // Check if the status code is 200 OK
            if (xhr.status == 200) {
                // Display a success alert message with the response text
                alert("Success: " + xhr.responseText);
                // Reload the window object to refresh the page
                window.location.reload();
            } else {
                // Display an error alert message with the status code and response text
                alert("Error: " + xhr.status + " - " + xhr.responseText);
            }
        });
        // Send the request with the form data
        xhr.send(formData);
    });
</script>

C++ code

// This route is used to create a new task from form data
CROW_ROUTE(app, "/client-mode/tasks/new").methods("POST"_method)([](crow::request req, crow::response& res) {
    // Get the content type from the request header
    std::string content_type = req.get_header_value("Content-Type");
    // Check if the content type is multipart/form-data
    if (content_type.find("multipart/form-data") != std::string::npos) {
        try {
            // Parse the request body using the crow::multipart::message class
            crow::multipart::message msg(req);
            std::cout << "Ok3" << std::endl;
            // Loop through the parts of the message
            for (const auto& part : msg.parts) {
                std::cout << "Part" << std::endl;
                std::string task_name = crow::multipart::get_header_object(part.headers, "name").value;
                std::cout << task_name << std::endl;
                // Check if the part has a filename, which means it is a file
                if (!crow::multipart::get_header_object(part.headers, "filename").value.empty()) {
                    std::cout << crow::multipart::get_header_object(part.headers, "filename").value << std::endl;
                    // Get the file name, data, and size
                    std::string file_name = crow::multipart::get_header_object(part.headers, "filename").value;
                    const char* file_data = part.body.c_str();
                    size_t file_size = part.body.size();
                    // Saving file to files folder
                    try {
                        std::ofstream out(get_files_folder() + "files/" + file_name, std::ios::binary);
                        out.write(file_data, file_size);
                        out.close();
                    } catch (const std::ios_base::failure& e) {
                        // Catch any file operation failures and print the error message
                        std::cout << "File operation failed: " << e.what() << std::endl;
                    }
                } else {
                    std::cout << "Empty part" << std::endl;
                }
            }
            // Return a response to the client with a success message and a status code of 200 OK
            res.code = 201;
            res.write("File uploaded successfully"); // Change here
            res.end();
        } catch (const std::exception& e) {
            // Catch any general exceptions and print the error message
            std::cout << "Exception occurred: " << e.what() << std::endl;
            // Return a response to the client with an error message and a status code of 500 Internal Server Error
            res.code = 500;
            res.write("Internal server error"); // Change here
            res.end();
        }
    } else {
        // Return a response to the client with an error message and a status code of 400 Bad Request
        res.code = 400;
        res.write("Invalid content type"); // Change here
        res.end();
    }
});

I am using Crow v1.0+5.

I've tried to check if the req object was a null object, since it is a core dump to see if that is the cause of the problem. It is actually not one because the Content-Type is available.

1

There are 1 best solutions below

1
GetSet On

In continuation from comments, that space between crow::multipart::message and msg(req) on its face looks odd.

Replace this line:

crow::multipart::message msg(req);

With this line:

crow::multipart::message::message(req);

There doesn't appear to be any method called msg() but one called message() which does indeed mutate in place.

Documentation:

https://crowcpp.org/master/reference/structcrow_1_1multipart_1_1message.html

Hope this helps. I cant claim it will solve all your problems but hopefully gets things moving forward.