| # Commonly recurring errors in bmcweb | 
 |  | 
 | What follows is a list of common errors that new users to bmcweb tend to make | 
 | when operating within its bounds for the first time. If this is your first time | 
 | developing in bmcweb, the maintainers highly recommend reading and understanding | 
 | _all_ of common traps before continuing with any development. Every single one | 
 | of the examples below compile without warnings, but are incorrect in | 
 | not-always-obvious ways, or impose a pattern that tends to cause hard to find | 
 | bugs, or bugs that appear later. Every one has been submitted to code review | 
 | multiple times. | 
 |  | 
 | ## 1. Directly dereferencing a pointer without checking for validity first | 
 |  | 
 | ```cpp | 
 | int myBadMethod(const nlohmann::json& j){ | 
 |     const int* myPtr = j.get_if<int>(); | 
 |     return *myPtr; | 
 | } | 
 | ``` | 
 |  | 
 | This pointer is not guaranteed to be filled, and could be a null dereference. | 
 |  | 
 | ## 2. String views aren't null terminated | 
 |  | 
 | ```cpp | 
 | int getIntFromString(std::string_view s){ | 
 |     return std::atoi(s.data()); | 
 | } | 
 | ``` | 
 |  | 
 | This will give the right answer much of the time, but has the possibility to | 
 | fail when `string_view` is not null terminated. Use `from_chars` instead, which | 
 | takes both a pointer and a length | 
 |  | 
 | ## 3. Not handling input errors | 
 |  | 
 | ```cpp | 
 | int getIntFromString(const std::string& s){ | 
 |     return std::atoi(s.c_str()); | 
 | } | 
 | ``` | 
 |  | 
 | In the case where the string is not representable as an int, this will trigger | 
 | undefined behavior at system level. Code needs to check for validity of the | 
 | string, ideally with something like `from_chars`, and return the appropriate | 
 | error code. | 
 |  | 
 | ## 4. Walking off the end of a string | 
 |  | 
 | ```cpp | 
 | std::string getFilenameFromPath(const std::string& path){ | 
 |     size_t index = path.find("/"); | 
 |     if (index != std::string::npos){ | 
 |         // If the string ends with "/", this will walk off the end of the string. | 
 |         return path.substr(pos + 1); | 
 |     } | 
 |     return ""; | 
 | } | 
 | ``` | 
 |  | 
 | ## 5. Using methods that throw (or not handling bad inputs) | 
 |  | 
 | ```cpp | 
 | int myBadMethod(nlohmann::json& j){ | 
 |     return j.get<int>(); | 
 | } | 
 | ``` | 
 |  | 
 | This method throws, and bad inputs will not be handled | 
 |  | 
 | Commonly used methods that fall into this pattern: | 
 |  | 
 | - std::variant::get | 
 | - std::vector::at | 
 | - std::map::at | 
 | - std::set::at | 
 | - std::\<generic container type\>::at | 
 | - nlohmann::json::operator!= | 
 | - nlohmann::json::operator+= | 
 | - nlohmann::json::at | 
 | - nlohmann::json::get | 
 | - nlohmann::json::get_ref | 
 | - nlohmann::json::get_to | 
 | - nlohmann::json::operator\<\< | 
 | - nlohmann::json::operator\>\> | 
 | - std::filesystem::create_directory | 
 | - std::filesystem::rename | 
 | - std::filesystem::file_size | 
 | - std::stoi | 
 | - std::stol | 
 | - std::stoll | 
 |  | 
 | ### Special note: JSON | 
 |  | 
 | `nlohmann::json::parse` by default | 
 | [throws](https://json.nlohmann.me/api/basic_json/parse/) on failure, but also | 
 | accepts an optional argument that causes it to not throw: set the 3rd argument | 
 | to `false`. | 
 |  | 
 | `nlohmann::json::dump` by default | 
 | [throws](https://json.nlohmann.me/api/basic_json/dump/) on failure, but also | 
 | accepts an optional argument that causes it to not throw: set the 4th argument | 
 | to `replace`. Although `ignore` preserves content 1:1, `replace` is preferred | 
 | from a security point of view. | 
 |  | 
 | ### Special note: Boost | 
 |  | 
 | there is a whole class of boost asio functions that provide both a method that | 
 | throws on failure, and a method that accepts and returns an error code. This is | 
 | not a complete list, but users should verify in the boost docs when calling into | 
 | asio methods, and prefer the one that returns an error code instead of throwing. | 
 |  | 
 | - boost::asio::ip::tcp::acceptor::bind(); | 
 | - boost::asio::ip::tcp::acceptor::cancel(); | 
 | - boost::asio::ip::tcp::acceptor::close(); | 
 | - boost::asio::ip::tcp::acceptor::listen(); | 
 | - boost::asio::ip::address::make_address(); | 
 |  | 
 | ## 6. Blocking functions | 
 |  | 
 | bmcweb uses a single reactor for all operations. Blocking that reactor for any | 
 | amount of time causes all other operations to stop. The common blocking | 
 | functions that tend to be called incorrectly are: | 
 |  | 
 | - sleep() | 
 | - boost::asio::ip::tcp::socket::read() | 
 | - boost::asio::ip::tcp::socket::read_some() | 
 | - boost::asio::ip::tcp::socket::write() | 
 | - boost::asio::ip::tcp::socket::write_some() | 
 | - boost::asio::ip::tcp::socket::connect() | 
 | - boost::asio::ip::tcp::socket::send() | 
 | - boost::asio::ip::tcp::socket::wait() | 
 | - boost::asio::steady_timer::wait() | 
 |  | 
 | Note: an exception is made for filesystem/disk IO read and write. This is mostly | 
 | due to not having great abstractions for it that mate well with the async | 
 | system, the fact that most filesystem accesses are into tmpfs (and therefore | 
 | should be "fast" most of the time) and in general how little the filesystem is | 
 | used in practice. | 
 |  | 
 | ## 7. Lack of locking between subsequent calls | 
 |  | 
 | While global data structures are discouraged, they are sometimes required to | 
 | store temporary state for operations that require it. Given the single threaded | 
 | nature of bmcweb, they are not required to be explicitly threadsafe, but they | 
 | must be always left in a valid state, and checked for other uses before | 
 | occupying. | 
 |  | 
 | ```cpp | 
 | std::optional<std::string> currentOperation; | 
 | void firstCallbackInFlow(){ | 
 |     currentOperation = "Foo"; | 
 | } | 
 | void secondCallbackInFlow(){ | 
 |     currentOperation.reset(); | 
 | } | 
 | ``` | 
 |  | 
 | In the above case, the first callback needs a check to ensure that | 
 | currentOperation is not already being used. | 
 |  | 
 | ## 8. Wildcard reference captures in lambdas | 
 |  | 
 | ```cpp | 
 | std::string x; auto mylambda = [&](){ | 
 |     x = "foo"; | 
 | } | 
 | do_async_read(mylambda) | 
 | ``` | 
 |  | 
 | Numerous times, lifetime issues of const references have been injected into | 
 | async bmcweb code. While capturing by reference can be useful, given how | 
 | difficult these types of bugs are to triage, bmcweb explicitly requires that all | 
 | code captures variables by name explicitly, and calls out each variable being | 
 | captured by value or by reference. The above prototypes would change to | 
 | `[&x]()...` Which makes clear that x is captured, and its lifetime needs | 
 | tracked. | 
 |  | 
 | ## 9. URLs should end in "/" | 
 |  | 
 | ```cpp | 
 | BMCWEB("/foo/bar"); | 
 | ``` | 
 |  | 
 | Unless you explicitly have a reason not to (as there is one known exception | 
 | where the behavior must differ) all URL handlers should end in "/". The bmcweb | 
 | route handler will detect routes ending in slash and generate routes for both | 
 | the route ending in slash and the one without. This allows both URLs to be used | 
 | by users. While many specifications do not require this, it resolves a whole | 
 | class of bug that we've seen in the past. | 
 |  | 
 | ## 10. URLs constructed in aggregate | 
 |  | 
 | ```cpp | 
 | std::string routeStart = "/redfish/v1"; | 
 |  | 
 | BMCWEB_ROUTE(routestart + "/SessionService/") | 
 | ``` | 
 |  | 
 | Very commonly, bmcweb maintainers and contributors alike have to do audits of | 
 | all routes that are available, to verify things like security and documentation | 
 | accuracy. While these processes are largely manual, they can mostly be conducted | 
 | by a simple grep statement to search for urls in question. Doing the above makes | 
 | the route handlers no longer greppable, and complicates bmcweb patchsets as a | 
 | whole. | 
 |  | 
 | ## 11. Not responding to 404 | 
 |  | 
 | ```cpp | 
 | BMCWEB_ROUTE("/myendpoint/<str>", | 
 |     [](Request& req, Response& res, const std::string& id){ | 
 |      managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | 
 |           [asyncResp](const boost::system::error_code& ec, | 
 |                       const std::string& myProperty) { | 
 |               if (ec) | 
 |               { | 
 |                   messages::internalError(asyncResp->res); | 
 |                   return; | 
 |               } | 
 |               ... handle code | 
 |           }, | 
 |           "xyz.openbmc_project.Logging", | 
 |           "/xyz/openbmc_project/mypath/" + id, | 
 |           "xyz.MyInterface", "GetAll", ""); | 
 | }); | 
 | ``` | 
 |  | 
 | All bmcweb routes should handle 404 (not found) properly, and return it where | 
 | appropriate. 500 internal error is not a substitute for this, and should be only | 
 | used if there isn't a more appropriate error code that can be returned. This is | 
 | important, because a number of vulnerability scanners attempt injection attacks | 
 | in the form of `/myendpoint/foobar`, or `/myendpoint/#$*(%)&#%$)(\*&` in an | 
 | attempt to circumvent security. If the server returns 500 to any of these | 
 | requests, the security scanner logs it as an error for followup. While in | 
 | general these errors are benign, and not actually a real security threat, having | 
 | a clean security run allows maintainers to minimize the amount of time spent | 
 | triaging issues reported from these scanning tools. | 
 |  | 
 | An implementation of the above that handles 404 would look like: | 
 |  | 
 | ```cpp | 
 | BMCWEB_ROUTE("/myendpoint/<str>", | 
 |     [](Request& req, Response& res, const std::string& id){ | 
 |      managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( | 
 |           [asyncResp](const boost::system::error_code& ec, | 
 |                       const std::string& myProperty) { | 
 |               if (ec == <error code that gets returned by not found>){ | 
 |                   messages::resourceNotFound(res); | 
 |                   return; | 
 |               } | 
 |               if (ec) | 
 |               { | 
 |                   messages::internalError(asyncResp->res); | 
 |                   return; | 
 |               } | 
 |               ... handle code | 
 |           }, | 
 |           "xyz.openbmc_project.Logging", | 
 |           "/xyz/openbmc_project/mypath/" + id, | 
 |           "xyz.MyInterface", "GetAll", ""); | 
 | }); | 
 | ``` | 
 |  | 
 | Note: A more general form of this rule is that no handler should ever return 500 | 
 | on a working system, and any cases where 500 is found, can immediately be | 
 | assumed to be | 
 | [a bug in either the system, or bmcweb.](https://github.com/openbmc/bmcweb/blob/master/DEVELOPING.md#error-handling) | 
 |  | 
 | ## 12. Imprecise matching | 
 |  | 
 | ```cpp | 
 | void isInventoryPath(const std::string& path){ | 
 |     if (path.find("inventory")){ | 
 |         return true; | 
 |     } | 
 |     return false; | 
 | } | 
 | ``` | 
 |  | 
 | When matching dbus paths, HTTP fields, interface names, care should be taken to | 
 | avoid doing direct string containment matching. Doing so can lead to errors | 
 | where fan1 and fan11 both report to the same object, and cause behavior breaks | 
 | in subtle ways. | 
 |  | 
 | When using dbus paths, rely on the methods on `sdbusplus::message::object_path`. | 
 | When parsing HTTP field and lists, use the RFC7230 implementations from | 
 | boost::beast. | 
 |  | 
 | Other commonly misused methods are: boost::iequals. Unless the standard you're | 
 | implementing (as is the case in some HTTP fields) requires case insensitive | 
 | comparisons, casing should be obeyed, especially when relying on user-driven | 
 | data. | 
 |  | 
 | - boost::starts_with | 
 | - boost::ends_with | 
 | - std::string::starts_with | 
 | - std::string::ends_with | 
 | - std::string::rfind | 
 |  | 
 | The above methods tend to be misused to accept user data and parse various | 
 | fields from it. In practice, there tends to be better, purpose built methods for | 
 | removing just the field you need. | 
 |  | 
 | ## 13. Complete replacement of the response object | 
 |  | 
 | ```cpp | 
 | void getMembers(crow::Response& res){ | 
 |   res.jsonValue = {{"Value", 2}}; | 
 | } | 
 | ``` | 
 |  | 
 | In many cases, bmcweb is doing multiple async actions in parallel, and | 
 | orthogonal keys within the Response object might be filled in from another task. | 
 | Completely replacing the json object can lead to convoluted situations where the | 
 | output of the response is dependent on the _order_ of the asynchronous actions | 
 | completing, which cannot be guaranteed, and has many time caused bugs. | 
 |  | 
 | ```cpp | 
 | void getMembers(crow::Response& res){ | 
 |   res.jsonValue["Value"] = 2; | 
 | } | 
 | ``` | 
 |  | 
 | As an added benefit, this code is also more efficient, as it avoids the | 
 | intermediate object construction and the move, and as a result, produces smaller | 
 | binaries. | 
 |  | 
 | Note, another form of this error involves calling nlohmann::json::reset(), to | 
 | clear an object that's already been filled in. This has the potential to clear | 
 | correct data that was already filled in from other sources. |