Error handling and sqlite3_stmt cleanup

FossilOrigin-Name: 485452c07c34e0ba67a85afdcc404ed6819efc0ff1978d2d4d5c8381f196ee61
This commit is contained in:
nekobit 2022-10-09 19:27:08 +00:00
parent ecf5241d6d
commit c527e18c85
7 changed files with 110 additions and 7 deletions

View file

@ -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

View file

@ -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<SQLiteBindType_t> keys,
std::function<void(sqlite3_stmt*)> 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<const char*>(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;
}

View file

@ -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<SQLiteBindType_t> keys,
std::function<void(sqlite3_stmt*)> 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<SQLiteBindType_t> keys,
std::function<void(sqlite3_stmt*)> func);
SQLiteCacheIndex sqlite_compile_to_cache(SQLiteCacheIndex idx, std::string_view sql);
std::array<sqlite3_stmt*, SQLiteCacheIndex::COUNT> sql_cache;

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#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};
}

32
src/http/error_response.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#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);
}
}

View file

@ -38,6 +38,7 @@ namespace HTTP
REDIRECT_PERM = 308,
// 400
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,

View file

@ -17,6 +17,7 @@
*/
#include "jsonhelper.h"
#include <stdexcept>
#include <utility>
#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,