Error handling and sqlite3_stmt cleanup
FossilOrigin-Name: 485452c07c34e0ba67a85afdcc404ed6819efc0ff1978d2d4d5c8381f196ee61
This commit is contained in:
parent
ecf5241d6d
commit
c527e18c85
7 changed files with 110 additions and 7 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
28
src/http/error_response.cpp
Normal file
28
src/http/error_response.cpp
Normal 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
32
src/http/error_response.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -38,6 +38,7 @@ namespace HTTP
|
|||
REDIRECT_PERM = 308,
|
||||
|
||||
// 400
|
||||
BAD_REQUEST = 400,
|
||||
UNAUTHORIZED = 401,
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue