blob: d801d935a41706b5d9106ef80596d25a5eec9498 [file] [edit]
#include "node_config_file.h"
#include "debug_utils-inl.h"
#include "simdjson.h"
#include <string>
namespace node {
std::optional<std::string_view> ConfigReader::GetDataFromArgs(
const std::vector<std::string>& args) {
constexpr std::string_view flag_path = "--experimental-config-file";
constexpr std::string_view default_file =
"--experimental-default-config-file";
bool has_default_config_file = false;
for (auto it = args.begin(); it != args.end(); ++it) {
if (*it == flag_path) {
// Case: "--experimental-config-file foo"
if (auto next = std::next(it); next != args.end()) {
return *next;
}
} else if (it->starts_with(flag_path)) {
// Case: "--experimental-config-file=foo"
if (it->size() > flag_path.size() && (*it)[flag_path.size()] == '=') {
return it->substr(flag_path.size() + 1);
}
} else if (*it == default_file || it->starts_with(default_file)) {
has_default_config_file = true;
}
}
if (has_default_config_file) {
return "node.config.json";
}
return std::nullopt;
}
ParseResult ConfigReader::ParseNodeOptions(
simdjson::ondemand::object* node_options_object) {
auto env_options_map = options_parser::MapEnvOptionsFlagInputType();
simdjson::ondemand::value ondemand_value;
std::string_view key;
for (auto field : *node_options_object) {
if (field.unescaped_key().get(key) || field.value().get(ondemand_value)) {
return ParseResult::InvalidContent;
}
// The key needs to match the CLI option
std::string prefix = "--";
auto it = env_options_map.find(prefix.append(key));
if (it != env_options_map.end()) {
switch (it->second) {
case options_parser::OptionType::kBoolean: {
bool result;
if (ondemand_value.get_bool().get(result)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" +
(result ? "true" : "false"));
break;
}
// String array can allow both string and array types
case options_parser::OptionType::kStringList: {
simdjson::ondemand::json_type field_type;
if (ondemand_value.type().get(field_type)) {
return ParseResult::InvalidContent;
}
switch (field_type) {
case simdjson::ondemand::json_type::array: {
std::vector<std::string> result;
simdjson::ondemand::array raw_imports;
if (ondemand_value.get_array().get(raw_imports)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
for (auto raw_import : raw_imports) {
std::string_view import;
if (raw_import.get_string(import)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" + std::string(import));
}
break;
}
case simdjson::ondemand::json_type::string: {
std::string result;
if (ondemand_value.get_string(result)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" + result);
break;
}
default:
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
break;
}
case options_parser::OptionType::kString: {
std::string result;
if (ondemand_value.get_string(result)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" + result);
break;
}
case options_parser::OptionType::kInteger: {
int64_t result;
if (ondemand_value.get_int64().get(result)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" + std::to_string(result));
break;
}
case options_parser::OptionType::kHostPort:
case options_parser::OptionType::kUInteger: {
uint64_t result;
if (ondemand_value.get_uint64().get(result)) {
FPrintF(stderr, "Invalid value for %s\n", it->first.c_str());
return ParseResult::InvalidContent;
}
node_options_.push_back(it->first + "=" + std::to_string(result));
break;
}
case options_parser::OptionType::kNoOp: {
FPrintF(stderr,
"No-op flag %s is currently not supported\n",
it->first.c_str());
return ParseResult::InvalidContent;
break;
}
case options_parser::OptionType::kV8Option: {
FPrintF(stderr,
"V8 flag %s is currently not supported\n",
it->first.c_str());
return ParseResult::InvalidContent;
}
default:
UNREACHABLE();
}
} else {
FPrintF(stderr, "Unknown or not allowed option %s\n", key.data());
return ParseResult::InvalidContent;
}
}
return ParseResult::Valid;
}
ParseResult ConfigReader::ParseConfig(const std::string_view& config_path) {
std::string file_content;
// Read the configuration file
int r = ReadFileSync(&file_content, config_path.data());
if (r != 0) {
const char* err = uv_strerror(r);
FPrintF(
stderr, "Cannot read configuration from %s: %s\n", config_path, err);
return ParseResult::FileError;
}
// Parse the configuration file
simdjson::ondemand::parser json_parser;
simdjson::ondemand::document document;
if (json_parser.iterate(file_content).get(document)) {
FPrintF(stderr, "Can't parse %s\n", config_path.data());
return ParseResult::InvalidContent;
}
simdjson::ondemand::object main_object;
// If document is not an object, throw an error.
if (auto root_error = document.get_object().get(main_object)) {
if (root_error == simdjson::error_code::INCORRECT_TYPE) {
FPrintF(stderr,
"Root value unexpected not an object for %s\n\n",
config_path.data());
} else {
FPrintF(stderr, "Can't parse %s\n", config_path.data());
}
return ParseResult::InvalidContent;
}
simdjson::ondemand::object node_options_object;
// If "nodeOptions" is an object, parse it
if (auto node_options_error =
main_object["nodeOptions"].get_object().get(node_options_object)) {
if (node_options_error != simdjson::error_code::NO_SUCH_FIELD) {
FPrintF(stderr,
"\"nodeOptions\" value unexpected for %s\n\n",
config_path.data());
return ParseResult::InvalidContent;
}
} else {
return ParseNodeOptions(&node_options_object);
}
return ParseResult::Valid;
}
std::string ConfigReader::AssignNodeOptions() {
if (node_options_.empty()) {
return "";
} else {
DCHECK_GT(node_options_.size(), 0);
std::string acc;
acc.reserve(node_options_.size() * 2);
for (size_t i = 0; i < node_options_.size(); ++i) {
// The space is necessary at the beginning of the string
acc += " " + node_options_[i];
}
return acc;
}
}
size_t ConfigReader::GetFlagsSize() {
return node_options_.size();
}
} // namespace node