From e287de2d1c1db4a5ffc0cc0a0c5d03bb7e909500 Mon Sep 17 00:00:00 2001 From: Chloe Kudryavtsev Date: Fri, 1 Mar 2019 02:09:59 -0500 Subject: Abstract out storage This means that various redis failures will get caught. Also opens up the possibility of PUT (for naming). Also simplified health checks! And, of course, makes it easier to swap backends later, if needed. --- source/storage.d | 41 +++++++++++++++++++++++++++++++++++++++++ source/web.d | 36 +++++++++++++++--------------------- 2 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 source/storage.d diff --git a/source/storage.d b/source/storage.d new file mode 100644 index 0000000..730c5ab --- /dev/null +++ b/source/storage.d @@ -0,0 +1,41 @@ +module brpaste.storage; + +import vibe.vibe; + +class RedisStorage { + private RedisDatabase client; + + this(URL url = URL("redis://127.0.0.1")) { + client = connectRedisDB(url); + } + + void isDown() { + enforceHTTP(healthy, HTTPStatus.serviceUnavailable, "Redis is down."); + } + + bool healthy() { + try { + client.client.ping; + } catch (Exception e) { + return false; + } + return true; + } + + auto get(in string key) { + isDown; + enforceHTTP(client.exists(key), HTTPStatus.notFound, key ~ " not found."); + return client.get(key); + } + + void put(in string key, in string data) { + isDown; + client.set(key, data); + } + + void putCollision(in string key, in string data) { + isDown; + enforceHTTP(! client.exists(key), HTTPStatus.unprocessableEntity, key ~ " already exists."); + put(key, data); + } +} diff --git a/source/web.d b/source/web.d index eddd55e..16dbbb9 100644 --- a/source/web.d +++ b/source/web.d @@ -1,29 +1,31 @@ module brpaste.web; import brpaste.hash; +import brpaste.storage; import vibe.vibe; -RedisDatabase client; +RedisStorage store; -void id(HTTPServerRequest req, HTTPServerResponse res) { +string idCommon(in HTTPServerRequest req) { string id = req.params["id"]; + return store.get(id); +} + +void id(HTTPServerRequest req, HTTPServerResponse res) { string language = "none"; // TODO: rewrite the next two lines once #2273 is resolved if ("lang" in req.query) language = req.query["lang"]; else if (req.query.length > 0) language = req.query.byKey.front; - enforceHTTP(client.exists(id), HTTPStatus.notFound, "No paste under " ~ id ~ "."); - auto data = client.get(id); + auto data = idCommon(req); render!("code.dt", data, language)(res); } void rawId(HTTPServerRequest req, HTTPServerResponse res) { - string id = req.params["id"]; - enforceHTTP(client.exists(id), HTTPStatus.notFound, "No paste under " ~ id ~ "."); - - auto data = client.get(id); res.contentType = "text/plain"; + + auto data = idCommon(req); res.writeBody(data); } @@ -32,31 +34,23 @@ void post(HTTPServerRequest req, HTTPServerResponse res) { auto data = req.form["data"]; auto hash = data.hash; - client.set(hash, data); + store.put(hash, data); res.statusCode = HTTPStatus.created; res.writeBody(hash); } void health(HTTPServerRequest req, HTTPServerResponse res) { res.statusCode = HTTPStatus.noContent; - scope(exit) res.writeBody(""); + scope(success) res.writeBody(""); // Redis - try { - client.client.ping; - } catch (Exception e) { - logCritical("Redis is down!"); - res.statusCode = HTTPStatus.serviceUnavailable; - res.statusPhrase = "Backend Storage Unavailable"; - res.headers["Retry-After"] = "60"; - } + store.isDown; } shared static this() { // setup redis - string path = "redis://127.0.0.1"; + string path; readOption("redis|r", &path, "The URL to use to connect to redis"); - URL redis = path; - client = connectRedisDB(redis); + store = path.empty ? new RedisStorage : new RedisStorage(URL(path)); } -- cgit v1.2.3