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.
In continuation from comments, that space between
crow::multipart::messageandmsg(req)on its face looks odd.Replace this line:
With this line:
There doesn't appear to be any method called
msg()but one calledmessage()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.