From c527e18c8556ff571a3fef97be353b7b3c728560 Mon Sep 17 00:00:00 2001 From: nekobit Date: Sun, 9 Oct 2022 19:27:08 +0000 Subject: [PATCH] Error handling and sqlite3_stmt cleanup FossilOrigin-Name: 485452c07c34e0ba67a85afdcc404ed6819efc0ff1978d2d4d5c8381f196ee61 --- CMakeLists.txt | 1 + src/database/sqlite/sqlite.cpp | 24 ++++++++++++++++++--- src/database/sqlite/sqlite.h | 17 ++++++++++++--- src/http/error_response.cpp | 28 ++++++++++++++++++++++++ src/http/error_response.h | 32 ++++++++++++++++++++++++++++ src/http/responsecode.h | 1 + src/protocol/webfinger/webfinger.cpp | 14 +++++++++++- 7 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 src/http/error_response.cpp create mode 100644 src/http/error_response.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9795e01..b8cb19c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(wormhole src/main.cpp src/http/request.cpp src/http/response.cpp src/http/mime.cpp + src/http/error_response.cpp src/config/config_http.cpp src/config/config_db.cpp src/config/config_loader.cpp diff --git a/src/database/sqlite/sqlite.cpp b/src/database/sqlite/sqlite.cpp index 3e63fe6..9b8e881 100644 --- a/src/database/sqlite/sqlite.cpp +++ b/src/database/sqlite/sqlite.cpp @@ -47,10 +47,15 @@ SQLite::SQLite(std::filesystem::path path) SQLite::~SQLite() { + // Close 'cached' statements + for (sqlite3_stmt* s: sql_cache) + { + sqlite3_finalize(s); + } sqlite3_close(db); } -void SQLite::sqlite_exec(SQLiteCacheIndex idx, +int SQLite::sqlite_exec(SQLiteCacheIndex idx, std::initializer_list keys, std::function func) { @@ -62,6 +67,8 @@ void SQLite::sqlite_exec(SQLiteCacheIndex idx, Logger::instance().log("Executing query: "s + sqlite3_sql(stmt), Logger::Level::DEBUG); + int query_count = 0; + int key_i = 1; for (auto&& key: keys) { @@ -90,6 +97,7 @@ void SQLite::sqlite_exec(SQLiteCacheIndex idx, while ((code = sqlite3_step(stmt)) == SQLITE_ROW) { func(stmt); + ++query_count; } switch (code) @@ -110,6 +118,7 @@ void SQLite::sqlite_exec(SQLiteCacheIndex idx, } sqlite3_reset(stmt); + return query_count; } SQLiteCacheIndex SQLite::sqlite_compile_to_cache(SQLiteCacheIndex idx, std::string_view sql) @@ -135,10 +144,14 @@ Types::User SQLite::get_user(unsigned long id) // Setup function sqlite_compile_to_cache(GET_USER_BY_ID, "SELECT acct FROM users WHERE id=?1"); - sqlite_exec(GET_USER_BY_ID, {{id}}, [&luser](sqlite3_stmt* stmt){ + int code = sqlite_exec(GET_USER_BY_ID, {{id}}, [&luser](sqlite3_stmt* stmt){ luser.acct = reinterpret_cast(sqlite3_column_text(stmt, 0)); }); + if (!code) + { + } + return luser; } @@ -148,10 +161,15 @@ Types::User SQLite::get_user(const std::string& acct) // Setup function sqlite_compile_to_cache(GET_USER_BY_ACCT, "SELECT acct FROM users WHERE acct=?1"); + - sqlite_exec(GET_USER_BY_ID, {acct}, [&luser](sqlite3_stmt* stmt){ + int code = sqlite_exec(GET_USER_BY_ID, {acct}, [&luser](sqlite3_stmt* stmt){ luser.id = sqlite3_column_int64(stmt, 0); }); + + if (!code) + { + } return luser; } diff --git a/src/database/sqlite/sqlite.h b/src/database/sqlite/sqlite.h index b88c7de..5a93d16 100644 --- a/src/database/sqlite/sqlite.h +++ b/src/database/sqlite/sqlite.h @@ -51,9 +51,20 @@ namespace DB virtual Types::User get_user(unsigned long id) override; virtual Types::User get_user(const std::string& acct) override; private: - void sqlite_exec(SQLiteCacheIndex idx, - std::initializer_list keys, - std::function func); + /** + * @brief Executes an SQL statement + * + * Similar to sqlite3_exec(), but supports the statement caching + * + * @param idx SQLite cached statement + * @param keys List of arguments to bind to the statement + * @param func Callback for each row + * + * @return Number of rows iterated, 0 if none. + */ + int sqlite_exec(SQLiteCacheIndex idx, + std::initializer_list keys, + std::function func); SQLiteCacheIndex sqlite_compile_to_cache(SQLiteCacheIndex idx, std::string_view sql); std::array sql_cache; diff --git a/src/http/error_response.cpp b/src/http/error_response.cpp new file mode 100644 index 0000000..d77b011 --- /dev/null +++ b/src/http/error_response.cpp @@ -0,0 +1,28 @@ +/* + * Wormhole - Federated social network + * Copyright (C) 2022 Nekobit + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "error_response.h" +#include "jsonhelper.h" +#include "mime.h" + +HTTP::Response HTTP::Error::make_json_error_response(const std::string& error, HTTP::Code code) +{ + rjson::Document root(rjson::kObjectType); + root.AddMember("error", error, root.GetAllocator()); + return HTTP::Response{ rjson::to_string(root), HTTP::MIME::JSON, code}; +} diff --git a/src/http/error_response.h b/src/http/error_response.h new file mode 100644 index 0000000..7dd70ef --- /dev/null +++ b/src/http/error_response.h @@ -0,0 +1,32 @@ +/* + * Wormhole - Federated social network + * Copyright (C) 2022 Nekobit + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include "responsecode.h" +#include "response.h" + +namespace HTTP +{ + namespace Error + { + HTTP::Response make_json_error_response(const std::string& error, HTTP::Code code = HTTP::Code::BAD_REQUEST); + } + +} diff --git a/src/http/responsecode.h b/src/http/responsecode.h index d001a83..c0526b9 100644 --- a/src/http/responsecode.h +++ b/src/http/responsecode.h @@ -38,6 +38,7 @@ namespace HTTP REDIRECT_PERM = 308, // 400 + BAD_REQUEST = 400, UNAUTHORIZED = 401, FORBIDDEN = 403, NOT_FOUND = 404, diff --git a/src/protocol/webfinger/webfinger.cpp b/src/protocol/webfinger/webfinger.cpp index e6fac2d..f62b673 100644 --- a/src/protocol/webfinger/webfinger.cpp +++ b/src/protocol/webfinger/webfinger.cpp @@ -17,6 +17,7 @@ */ #include "jsonhelper.h" +#include #include #include "http/mime.h" #include "http/response.h" @@ -25,6 +26,7 @@ #include "http/httpserver.h" #include "http/request.h" #include "logger.h" +#include "http/error_response.h" using namespace Protocol; @@ -55,8 +57,18 @@ HTTP::Response Route::webfinger(std::any& args, const HTTP::Request& req, const rjson::Document root(rjson::kObjectType); rjson::Document::AllocatorType& a = root.GetAllocator(); rjson::Value links(rjson::kArrayType); + std::string resource; - const std::string& resource = req.get.at("resource").string(); + try + { + resource = std::move(req.get.at("resource").string()); + } + catch (const std::out_of_range& err) { + return HTTP::Error::make_json_error_response("Missing resource parameter", HTTP::Code::BAD_REQUEST); + } + catch (...) { + return HTTP::Error::make_json_error_response("Couldn't find user", HTTP::Code::NOT_FOUND); + } // Add profile page links.PushBack(make_resource({"https://localhost/users/"s + resource,