2020-12-13 03:42:00 +00:00
|
|
|
use std::{
|
2020-12-14 07:07:13 +00:00
|
|
|
collections::HashMap,
|
2020-12-13 03:42:00 +00:00
|
|
|
};
|
|
|
|
|
2020-12-14 07:07:13 +00:00
|
|
|
use chrono::{DateTime, Utc};
|
2020-12-13 03:42:00 +00:00
|
|
|
use hyper::{
|
|
|
|
Body,
|
|
|
|
Request,
|
|
|
|
Response,
|
|
|
|
StatusCode,
|
|
|
|
};
|
2020-12-14 07:07:13 +00:00
|
|
|
use serde::{
|
|
|
|
Serialize,
|
|
|
|
Serializer,
|
|
|
|
};
|
2020-12-13 03:42:00 +00:00
|
|
|
use tracing::{
|
|
|
|
error,
|
|
|
|
instrument,
|
|
|
|
};
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
RequestError,
|
|
|
|
error_reply,
|
2021-03-21 03:34:47 +00:00
|
|
|
key_validity::{
|
|
|
|
BlakeHashWrapper,
|
|
|
|
KeyValidity,
|
|
|
|
},
|
2021-04-18 13:28:07 +00:00
|
|
|
relay_state::Relay,
|
2020-12-13 03:42:00 +00:00
|
|
|
};
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
// Not sure if this is the best way to do a hard-coded string table, but
|
|
|
|
// it'll keep the tests up to date
|
|
|
|
|
|
|
|
mod strings {
|
|
|
|
pub const FORBIDDEN: &str = "403 Forbidden";
|
|
|
|
pub const NO_API_KEY: &str = "Can't auth as scraper without API key";
|
|
|
|
pub const UNKNOWN_API_VERSION: &str = "Unknown scraper API version";
|
|
|
|
pub const UNKNOWN_API_ENDPOINT: &str = "Unknown scraper API endpoint";
|
|
|
|
}
|
|
|
|
|
2020-12-14 07:07:13 +00:00
|
|
|
// JSON is probably Good Enough For Now, so I'll just make everything
|
|
|
|
// a struct and lazily serialize it right before leaving the
|
|
|
|
// top-level handle () fn.
|
|
|
|
|
|
|
|
fn serialize_last_seen <S: Serializer> (x: &Option <DateTime <Utc>>, s: S)
|
|
|
|
-> Result <S::Ok, S::Error>
|
|
|
|
{
|
|
|
|
match x {
|
|
|
|
None => s.serialize_none (),
|
|
|
|
Some (x) => s.serialize_str (&x.to_rfc3339 ()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive (Serialize)]
|
|
|
|
pub struct Server {
|
|
|
|
pub name: String,
|
|
|
|
pub display_name: String,
|
|
|
|
#[serde (serialize_with = "serialize_last_seen")]
|
|
|
|
pub last_seen: Option <DateTime <Utc>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive (Serialize)]
|
|
|
|
pub struct ServerList {
|
|
|
|
pub servers: Vec <Server>,
|
|
|
|
}
|
|
|
|
|
2021-05-02 21:59:04 +00:00
|
|
|
pub async fn v1_server_list (state: &Relay)
|
2020-12-14 07:07:13 +00:00
|
|
|
-> ServerList
|
|
|
|
{
|
|
|
|
// name --> display_name
|
2021-05-02 22:23:12 +00:00
|
|
|
let mut display_names = HashMap::new ();
|
|
|
|
|
|
|
|
{
|
2020-12-14 07:07:13 +00:00
|
|
|
let guard = state.config.read ().await;
|
|
|
|
|
2021-05-02 22:23:12 +00:00
|
|
|
for (k, v) in &guard.servers {
|
2020-12-14 07:07:13 +00:00
|
|
|
let display_name = v.display_name
|
|
|
|
.clone ()
|
|
|
|
.unwrap_or_else (|| k.clone ());
|
|
|
|
|
2021-05-02 22:23:12 +00:00
|
|
|
display_names.insert (k.clone (), display_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let guard = state.me_config.read ().await;
|
2020-12-14 07:07:13 +00:00
|
|
|
|
2021-05-02 22:23:12 +00:00
|
|
|
for (k, v) in &guard.servers {
|
|
|
|
let display_name = v.display_name
|
|
|
|
.clone ()
|
|
|
|
.unwrap_or_else (|| k.clone ());
|
|
|
|
|
|
|
|
display_names.insert (k.clone (), display_name);
|
|
|
|
}
|
|
|
|
}
|
2020-12-14 07:07:13 +00:00
|
|
|
|
|
|
|
// name --> status
|
|
|
|
let server_statuses = {
|
|
|
|
let guard = state.server_status.lock ().await;
|
|
|
|
(*guard).clone ()
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut servers: Vec <_> = display_names.into_iter ()
|
|
|
|
.map (|(name, display_name)| {
|
|
|
|
let last_seen = server_statuses.get (&name).map (|x| x.last_seen);
|
|
|
|
|
|
|
|
Server {
|
|
|
|
display_name,
|
|
|
|
name,
|
|
|
|
last_seen,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect ();
|
|
|
|
|
|
|
|
servers.sort_by (|a, b| a.display_name.cmp (&b.display_name));
|
|
|
|
|
|
|
|
ServerList {
|
|
|
|
servers,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-29 22:13:53 +00:00
|
|
|
fn get_api_key (headers: &hyper::HeaderMap) -> Option <&str>
|
|
|
|
{
|
|
|
|
if let Some (key) = headers.get ("X-ApiKey").and_then (|v| v.to_str ().ok ()) {
|
|
|
|
return Some (key);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some (s) = headers.get ("Authorization").and_then (|v| v.to_str ().ok ()) {
|
|
|
|
if let Some (key) = s.strip_prefix ("Bearer ") {
|
|
|
|
return Some (key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-12-13 03:42:00 +00:00
|
|
|
#[instrument (level = "trace", skip (req, state))]
|
2020-12-14 07:07:13 +00:00
|
|
|
async fn api_v1 (
|
2020-12-13 03:42:00 +00:00
|
|
|
req: Request <Body>,
|
2021-05-02 21:59:04 +00:00
|
|
|
state: &Relay,
|
2020-12-13 03:42:00 +00:00
|
|
|
path_rest: &str
|
|
|
|
)
|
|
|
|
-> Result <Response <Body>, RequestError>
|
|
|
|
{
|
2021-08-28 01:28:34 +00:00
|
|
|
use crate::{
|
|
|
|
AuditData,
|
|
|
|
AuditEvent,
|
|
|
|
};
|
|
|
|
|
2022-07-29 22:13:53 +00:00
|
|
|
let api_key = get_api_key (req.headers ());
|
2020-12-13 03:42:00 +00:00
|
|
|
|
|
|
|
let api_key = match api_key {
|
2020-12-16 14:46:03 +00:00
|
|
|
None => return Ok (error_reply (StatusCode::FORBIDDEN, strings::NO_API_KEY)?),
|
2020-12-13 03:42:00 +00:00
|
|
|
Some (x) => x,
|
|
|
|
};
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
let actual_hash = BlakeHashWrapper::from_key (api_key.as_bytes ());
|
|
|
|
|
|
|
|
let bad_key = || error_reply (StatusCode::FORBIDDEN, strings::FORBIDDEN);
|
2020-12-13 03:42:00 +00:00
|
|
|
|
2021-08-28 01:28:34 +00:00
|
|
|
let key_name;
|
|
|
|
|
2020-12-13 03:42:00 +00:00
|
|
|
{
|
|
|
|
let config = state.config.read ().await;
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
let expected_key = match config.scraper_keys.get (&actual_hash.encode_base64 ()) {
|
2020-12-13 03:42:00 +00:00
|
|
|
Some (x) => x,
|
|
|
|
None => return Ok (bad_key ()?),
|
|
|
|
};
|
|
|
|
|
|
|
|
let now = chrono::Utc::now ();
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
// The hash in is_valid is redundant
|
2020-12-13 03:42:00 +00:00
|
|
|
match expected_key.is_valid (now, api_key.as_bytes ()) {
|
|
|
|
KeyValidity::Valid => (),
|
|
|
|
KeyValidity::WrongKey (bad_hash) => {
|
|
|
|
error! ("Bad scraper key with hash {:?}", bad_hash);
|
|
|
|
return Ok (bad_key ()?);
|
|
|
|
}
|
|
|
|
err => {
|
|
|
|
error! ("Bad scraper key {:?}", err);
|
|
|
|
return Ok (bad_key ()?);
|
|
|
|
},
|
|
|
|
}
|
2021-08-28 01:28:34 +00:00
|
|
|
|
|
|
|
key_name = expected_key.name.to_string ();
|
2020-12-13 03:42:00 +00:00
|
|
|
}
|
|
|
|
|
2021-08-28 01:28:34 +00:00
|
|
|
state.audit_log.push (AuditEvent::new (AuditData::ScraperGet {
|
|
|
|
key_name,
|
|
|
|
path: path_rest.to_string (),
|
|
|
|
})).await;
|
|
|
|
|
2022-07-29 21:45:10 +00:00
|
|
|
if path_rest == "metrics" {
|
|
|
|
Ok (metrics (req, state).await?)
|
|
|
|
}
|
|
|
|
else if path_rest == "test" {
|
2020-12-13 03:42:00 +00:00
|
|
|
Ok (error_reply (StatusCode::OK, "You're valid!")?)
|
|
|
|
}
|
2020-12-14 07:07:13 +00:00
|
|
|
else if path_rest == "server_list" {
|
|
|
|
let x = v1_server_list (&state).await;
|
|
|
|
Ok (error_reply (StatusCode::OK, &serde_json::to_string (&x).unwrap ())?)
|
|
|
|
}
|
2021-04-18 01:50:48 +00:00
|
|
|
else if let Some (rest) = path_rest.strip_prefix ("server/") {
|
2020-12-16 14:46:03 +00:00
|
|
|
// DRY T4H76LB3
|
|
|
|
|
|
|
|
if let Some (idx) = rest.find ('/') {
|
|
|
|
let listen_code = String::from (&rest [0..idx]);
|
|
|
|
let path = String::from (&rest [idx..]);
|
|
|
|
let (parts, _) = req.into_parts ();
|
|
|
|
|
|
|
|
// This is ugly. I don't like having scraper_api know about the
|
|
|
|
// crate root.
|
|
|
|
|
2021-05-02 21:59:04 +00:00
|
|
|
Ok (crate::handle_http_request (parts, path, &state, &listen_code).await?)
|
2020-12-16 14:46:03 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?)
|
|
|
|
}
|
|
|
|
}
|
2020-12-13 03:42:00 +00:00
|
|
|
else {
|
2020-12-16 14:46:03 +00:00
|
|
|
Ok (error_reply (StatusCode::NOT_FOUND, strings::UNKNOWN_API_ENDPOINT)?)
|
2020-12-13 03:42:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 22:31:28 +00:00
|
|
|
#[instrument (level = "trace", skip (req, state))]
|
|
|
|
async fn metrics (
|
|
|
|
req: Request <Body>,
|
|
|
|
state: &Relay,
|
|
|
|
)
|
|
|
|
-> Result <Response <Body>, RequestError>
|
|
|
|
{
|
|
|
|
let mut s = String::with_capacity (4 * 1_024);
|
2022-07-29 22:02:25 +00:00
|
|
|
|
2022-07-27 22:31:28 +00:00
|
|
|
s.push_str ("# HELP forty_two Forty-two\n");
|
|
|
|
s.push_str ("# TYPE forty_two counter\n");
|
|
|
|
s.push_str ("forty_two 42\n");
|
|
|
|
|
2022-07-29 22:02:25 +00:00
|
|
|
#[cfg (target_os = "linux")]
|
|
|
|
{
|
|
|
|
if let Some (rss) = tokio::fs::read_to_string ("/proc/self/status").await
|
|
|
|
.ok ()
|
|
|
|
.and_then (|s| get_rss_from_status (s.as_str ()))
|
|
|
|
{
|
|
|
|
s.push_str ("# HELP relay_vm_rss VmRSS of the relay process, in kB\n");
|
|
|
|
s.push_str ("# TYPE relay_vm_rss gauge\n");
|
|
|
|
s.push_str (format! ("relay_vm_rss {}\n", rss).as_str ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 22:31:28 +00:00
|
|
|
Ok (Response::builder ()
|
|
|
|
.body (Body::from (s))?)
|
|
|
|
}
|
|
|
|
|
2020-12-13 03:42:00 +00:00
|
|
|
#[instrument (level = "trace", skip (req, state))]
|
2020-12-14 07:07:13 +00:00
|
|
|
pub async fn handle (
|
2020-12-13 03:42:00 +00:00
|
|
|
req: Request <Body>,
|
2021-05-02 21:59:04 +00:00
|
|
|
state: &Relay,
|
2020-12-13 03:42:00 +00:00
|
|
|
path_rest: &str
|
|
|
|
)
|
|
|
|
-> Result <Response <Body>, RequestError>
|
|
|
|
{
|
|
|
|
{
|
2020-12-14 07:07:13 +00:00
|
|
|
if ! state.config.read ().await.iso.enable_scraper_api {
|
2020-12-13 03:42:00 +00:00
|
|
|
return Ok (error_reply (StatusCode::FORBIDDEN, "Scraper API disabled")?);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 01:50:48 +00:00
|
|
|
if let Some (rest) = path_rest.strip_prefix ("v1/") {
|
2020-12-14 07:07:13 +00:00
|
|
|
api_v1 (req, state, rest).await
|
2020-12-13 03:42:00 +00:00
|
|
|
}
|
2021-04-18 01:50:48 +00:00
|
|
|
else if let Some (rest) = path_rest.strip_prefix ("api/") {
|
2020-12-14 07:07:13 +00:00
|
|
|
api_v1 (req, state, rest).await
|
2020-12-13 03:42:00 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-12-16 14:46:03 +00:00
|
|
|
Ok (error_reply (StatusCode::NOT_FOUND, strings::UNKNOWN_API_VERSION)?)
|
2020-12-13 03:42:00 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-13 04:03:30 +00:00
|
|
|
|
2022-07-29 21:56:45 +00:00
|
|
|
fn get_rss_from_status (proc_status: &str) -> Option <u64>
|
|
|
|
{
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
for line in proc_status.lines () {
|
|
|
|
if let Some (rest) = line.strip_prefix ("VmRSS:\t").and_then (|s| s.strip_suffix (" kB"))
|
|
|
|
{
|
|
|
|
return u64::from_str (rest.trim_start ()).ok ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2020-12-13 04:03:30 +00:00
|
|
|
#[cfg (test)]
|
|
|
|
mod tests {
|
|
|
|
use std::{
|
2021-04-27 20:05:27 +00:00
|
|
|
convert::{TryInto},
|
2020-12-13 04:03:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use tokio::runtime::Runtime;
|
|
|
|
use crate::{
|
|
|
|
key_validity,
|
|
|
|
};
|
|
|
|
use super::*;
|
|
|
|
|
2020-12-13 04:44:37 +00:00
|
|
|
#[derive (Clone)]
|
|
|
|
struct TestCase {
|
|
|
|
// Inputs
|
|
|
|
path_rest: &'static str,
|
2022-07-27 22:31:37 +00:00
|
|
|
auth_header: Option <&'static str>,
|
2020-12-13 04:44:37 +00:00
|
|
|
valid_key: Option <&'static str>,
|
2022-07-27 22:31:37 +00:00
|
|
|
x_api_key: Option <&'static str>,
|
2020-12-13 04:03:30 +00:00
|
|
|
|
2020-12-13 04:44:37 +00:00
|
|
|
// Expected
|
|
|
|
expected_status: StatusCode,
|
|
|
|
expected_headers: Vec <(&'static str, &'static str)>,
|
2020-12-16 14:46:03 +00:00
|
|
|
expected_body: String,
|
2020-12-13 04:44:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TestCase {
|
|
|
|
fn path_rest (&self, v: &'static str) -> Self {
|
|
|
|
let mut x = self.clone ();
|
|
|
|
x.path_rest = v;
|
|
|
|
x
|
|
|
|
}
|
2020-12-13 04:03:30 +00:00
|
|
|
|
2020-12-13 04:44:37 +00:00
|
|
|
fn valid_key (&self, v: Option <&'static str>) -> Self {
|
|
|
|
let mut x = self.clone ();
|
|
|
|
x.valid_key = v;
|
|
|
|
x
|
|
|
|
}
|
|
|
|
|
2022-07-27 22:31:37 +00:00
|
|
|
fn auth_header (&self, v: Option <&'static str>) -> Self {
|
2020-12-13 04:44:37 +00:00
|
|
|
let mut x = self.clone ();
|
2022-07-27 22:31:37 +00:00
|
|
|
x.auth_header = v;
|
|
|
|
x
|
|
|
|
}
|
|
|
|
|
|
|
|
fn x_api_key (&self, v: Option <&'static str>) -> Self {
|
|
|
|
let mut x = self.clone ();
|
|
|
|
x.x_api_key = v;
|
2020-12-13 04:44:37 +00:00
|
|
|
x
|
|
|
|
}
|
2020-12-13 04:03:30 +00:00
|
|
|
|
2020-12-13 04:44:37 +00:00
|
|
|
fn expected_status (&self, v: StatusCode) -> Self {
|
|
|
|
let mut x = self.clone ();
|
|
|
|
x.expected_status = v;
|
|
|
|
x
|
|
|
|
}
|
2020-12-13 04:03:30 +00:00
|
|
|
|
2020-12-13 04:44:37 +00:00
|
|
|
fn expected_headers (&self, v: Vec <(&'static str, &'static str)>) -> Self {
|
|
|
|
let mut x = self.clone ();
|
|
|
|
x.expected_headers = v;
|
|
|
|
x
|
|
|
|
}
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
fn expected_body (&self, v: String) -> Self {
|
2020-12-13 04:44:37 +00:00
|
|
|
let mut x = self.clone ();
|
|
|
|
x.expected_body = v;
|
|
|
|
x
|
|
|
|
}
|
|
|
|
|
2020-12-16 14:46:03 +00:00
|
|
|
fn expected (&self, sc: StatusCode, body: &str) -> Self {
|
2020-12-13 04:44:37 +00:00
|
|
|
self
|
|
|
|
.expected_status (sc)
|
2020-12-16 14:46:03 +00:00
|
|
|
.expected_body (format! ("{}\n", body))
|
2020-12-13 04:44:37 +00:00
|
|
|
}
|
|
|
|
|
2022-07-29 22:13:53 +00:00
|
|
|
async fn test (&self, name: &str) {
|
2020-12-13 04:44:37 +00:00
|
|
|
let mut input = Request::builder ()
|
|
|
|
.method ("GET")
|
|
|
|
.uri (format! ("http://127.0.0.1:4000/scraper/{}", self.path_rest));
|
|
|
|
|
2022-07-27 22:31:37 +00:00
|
|
|
if let Some (auth_header) = self.auth_header {
|
|
|
|
input = input.header ("Authorization", auth_header);
|
|
|
|
}
|
|
|
|
if let Some (x_api_key) = self.x_api_key {
|
|
|
|
input = input.header ("X-ApiKey", x_api_key);
|
2020-12-13 04:44:37 +00:00
|
|
|
}
|
|
|
|
let input = input.body (Body::empty ()).unwrap ();
|
|
|
|
|
2021-04-27 20:05:27 +00:00
|
|
|
let builder = Relay::build ()
|
|
|
|
.port (4000)
|
|
|
|
.enable_scraper_api (true);
|
|
|
|
|
|
|
|
let builder = if let Some (key) = self.valid_key.map (|x| key_validity::ScraperKey::new_30_day ("automated test", x.as_bytes ())) {
|
|
|
|
builder.scraper_key (key)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
builder
|
2020-12-13 04:44:37 +00:00
|
|
|
};
|
|
|
|
|
2021-04-27 20:05:27 +00:00
|
|
|
let relay_state = builder.build ().expect ("Can't create relay state");
|
2020-12-13 04:44:37 +00:00
|
|
|
|
2021-05-02 21:59:04 +00:00
|
|
|
let actual = super::handle (input, &relay_state, self.path_rest).await;
|
2020-12-13 04:44:37 +00:00
|
|
|
let actual = actual.expect ("Relay didn't respond");
|
|
|
|
let (actual_head, actual_body) = actual.into_parts ();
|
|
|
|
|
|
|
|
let mut expected_headers = hyper::header::HeaderMap::new ();
|
|
|
|
|
|
|
|
for (key, value) in &self.expected_headers {
|
|
|
|
expected_headers.insert (*key, (*value).try_into ().expect ("Couldn't convert header value"));
|
|
|
|
}
|
|
|
|
|
2022-07-29 22:13:53 +00:00
|
|
|
assert_eq! (actual_head.status, self.expected_status, "{}", name);
|
|
|
|
assert_eq! (actual_head.headers, expected_headers, "{}", name);
|
2020-12-13 04:44:37 +00:00
|
|
|
|
|
|
|
let actual_body = hyper::body::to_bytes (actual_body).await;
|
|
|
|
let actual_body = actual_body.expect ("Body should be convertible to bytes");
|
|
|
|
let actual_body = actual_body.to_vec ();
|
|
|
|
let actual_body = String::from_utf8 (actual_body).expect ("Body should be UTF-8");
|
|
|
|
|
2022-07-29 22:13:53 +00:00
|
|
|
assert_eq! (actual_body, self.expected_body, "{}", name);
|
2020-12-13 04:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn auth () {
|
2021-04-18 13:33:07 +00:00
|
|
|
let rt = Runtime::new ().expect ("Can't create runtime for testing");
|
2020-12-13 04:03:30 +00:00
|
|
|
|
|
|
|
rt.block_on (async move {
|
2020-12-13 04:44:37 +00:00
|
|
|
let base_case = TestCase {
|
|
|
|
path_rest: "v1/test",
|
|
|
|
valid_key: Some ("bogus"),
|
2022-07-27 22:31:37 +00:00
|
|
|
auth_header: None,
|
|
|
|
x_api_key: Some ("bogus"),
|
2020-12-13 04:44:37 +00:00
|
|
|
expected_status: StatusCode::OK,
|
|
|
|
expected_headers: vec! [
|
|
|
|
("content-type", "text/plain"),
|
|
|
|
],
|
2020-12-16 14:46:03 +00:00
|
|
|
expected_body: "You're valid!\n".to_string (),
|
2020-12-13 04:44:37 +00:00
|
|
|
};
|
|
|
|
|
2022-07-27 22:31:37 +00:00
|
|
|
base_case
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("00").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.path_rest ("v9999/test")
|
|
|
|
.expected (StatusCode::NOT_FOUND, strings::UNKNOWN_API_VERSION)
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("01").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.valid_key (None)
|
|
|
|
.expected (StatusCode::FORBIDDEN, strings::FORBIDDEN)
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("02").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.x_api_key (Some ("borgus"))
|
|
|
|
.expected (StatusCode::FORBIDDEN, strings::FORBIDDEN)
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("03").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.path_rest ("v1/toast")
|
|
|
|
.expected (StatusCode::NOT_FOUND, strings::UNKNOWN_API_ENDPOINT)
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("04").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.x_api_key (None)
|
|
|
|
.expected (StatusCode::FORBIDDEN, strings::NO_API_KEY)
|
2022-07-29 22:13:53 +00:00
|
|
|
.test ("05").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
|
|
|
base_case
|
|
|
|
.x_api_key (None)
|
2022-07-29 22:13:53 +00:00
|
|
|
.auth_header (Some ("Bearer bogus"))
|
|
|
|
.expected (StatusCode::OK, "You're valid!")
|
|
|
|
.test ("06").await;
|
2022-07-27 22:31:37 +00:00
|
|
|
|
2020-12-13 04:03:30 +00:00
|
|
|
});
|
|
|
|
}
|
2022-07-29 21:56:45 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn rss () {
|
|
|
|
let input = "VmHWM: 584 kB\nVmRSS: 584 kB\nRssAnon: 68 kB\n";
|
|
|
|
|
|
|
|
assert_eq! (get_rss_from_status (input), Some (584));
|
|
|
|
}
|
2020-12-13 04:03:30 +00:00
|
|
|
}
|