Browse Source

auto-generated rate-limiting documentation

rate-limiting
Matthias Ahouansou 1 month ago
parent
commit
23cc1b022e
No known key found for this signature in database
  1. 3
      .cargo/config.toml
  2. 26
      Cargo.lock
  3. 3
      Cargo.toml
  4. 5
      conduit-config/Cargo.toml
  5. 13
      conduit-config/src/rate_limiting.rs
  6. 22
      conduit-macros/Cargo.toml
  7. 91
      conduit-macros/src/doc_generators.rs
  8. 16
      conduit-macros/src/lib.rs
  9. 2
      conduit/src/service/rate_limiting/mod.rs
  10. 19
      docs/configuration/rate-limiting.md
  11. 1
      flake.nix
  12. 4
      nix/pkgs/book/default.nix
  13. 7
      nix/pkgs/default/default.nix
  14. 12
      xtask/Cargo.toml
  15. 3
      xtask/src/main.rs

3
.cargo/config.toml

@ -1,2 +1,5 @@
[env]
RUMA_UNSTABLE_EXHAUSTIVE_TYPES = "1"
[alias]
xtask = "run --package xtask --"

26
Cargo.lock generated

@ -579,6 +579,7 @@ name = "conduit-config"
version = "0.11.0-alpha"
dependencies = [
"bytesize",
"conduit-macros",
"humantime-serde",
"reqwest",
"ruma",
@ -588,6 +589,15 @@ dependencies = [
"url",
]
[[package]]
name = "conduit-macros"
version = "0.11.0-alpha"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "const-oid"
version = "0.9.6"
@ -2296,9 +2306,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.95"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
@ -2363,9 +2373,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.40"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
@ -3252,9 +3262,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.104"
version = "2.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
dependencies = [
"proc-macro2",
"quote",
@ -4331,6 +4341,10 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "xtask"
version = "0.11.0-alpha"
[[package]]
name = "yansi"
version = "1.0.1"

3
Cargo.toml

@ -1,6 +1,6 @@
[workspace]
default-members = ["conduit"]
members = ["conduit", "conduit-config"]
members = ["conduit", "conduit-config", "conduit-macros", "xtask"]
resolver = "2"
[workspace.lints.rust]
@ -22,6 +22,7 @@ rust-version = "1.88.0"
[workspace.dependencies]
bytesize = "2"
conduit-config.path = "conduit-config"
conduit-macros.path = "conduit-macros"
reqwest = { version = "0.12", default-features = false }
ruma.git = "https://github.com/ruma/ruma.git"
rusty-s3 = "0.8"

5
conduit-config/Cargo.toml

@ -20,6 +20,8 @@ thiserror.workspace = true
rusty-s3.workspace = true
# Proxy config
reqwest.workspace = true
# Generating documentation
conduit-macros.workspace = true
# default room version, server name, ignored keys
[dependencies.ruma]
@ -30,5 +32,8 @@ workspace = true
rocksdb = []
sqlite = []
# Used to generate docs, shouldn't be used outside of xtask
doc-generators = ["conduit-macros/doc-generators"]
[lints]
workspace = true

13
conduit-config/src/rate_limiting.rs

@ -189,7 +189,16 @@ pub enum Restriction {
Federation(FederationRestriction),
}
#[cfg(feature = "doc-generators")]
pub trait DocumentRestrictions: Sized {
fn variant_doc_comments() -> Vec<(Self, String)>;
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
feature = "doc-generators",
derive(conduit_macros::DocumentRestrictions)
)]
#[serde(rename_all = "snake_case")]
pub enum ClientRestriction {
Registration,
@ -210,6 +219,10 @@ pub enum ClientRestriction {
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(
feature = "doc-generators",
derive(conduit_macros::DocumentRestrictions)
)]
#[serde(rename_all = "snake_case")]
pub enum FederationRestriction {
Join,

22
conduit-macros/Cargo.toml

@ -0,0 +1,22 @@
[package]
edition.workspace = true
homepage.workspace = true
name = "conduit-macros"
repository.workspace = true
rust-version.workspace = true
version = "0.11.0-alpha"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["full"] }
[features]
default = ["doc-generators"]
doc-generators = []
[lints]
workspace = true

91
conduit-macros/src/doc_generators.rs

@ -0,0 +1,91 @@
use std::{fmt::Display, path::Path};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{ToTokens, quote};
use syn::{Attribute, Expr, Field, Ident, ItemEnum, Lit, MetaNameValue, Variant, parse::Parse};
pub struct Restrictions {
ident: Ident,
variants: Vec<Restriction>,
}
pub struct Restriction {
ident: Ident,
doc_comments: Vec<String>,
}
impl Parse for Restrictions {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ItemEnum {
ident,
variants,
// Might be useful later.
attrs: _,
..
} = ItemEnum::parse(input)?;
let variants = variants
.into_iter()
.map(|Variant { attrs, ident, .. }| {
let doc_comments = attrs
.into_iter()
.filter_map(|attr| {
if let syn::Meta::NameValue(MetaNameValue { path, value, .. }) = attr.meta
&& path.is_ident("doc")
&& let Expr::Lit(lit) = value
&& let Lit::Str(string) = lit.lit
{
Some(string.value().trim().to_owned() + "\n")
} else {
None
}
})
.collect();
Ok(Restriction {
ident,
doc_comments,
})
})
.collect::<syn::Result<Vec<_>>>()?;
Ok(Self { ident, variants })
}
}
/// Produces the following function on said restriction:
/// - `variant_doc_comments`, returning each variant and it's doc comment.
impl ToTokens for Restrictions {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let Self { ident, variants } = self;
let output = quote! {
impl DocumentRestrictions for #ident {
fn variant_doc_comments() -> Vec<(Self, String)> {
vec![#( (#variants), )*]
}
}
};
tokens.extend(output);
}
}
impl ToTokens for Restriction {
fn to_tokens(&self, tokens: &mut TokenStream2) {
let Self {
ident,
doc_comments,
} = self;
let doc_comment = doc_comments
.iter()
.map(|attr| attr.trim())
.collect::<Vec<_>>()
.join("\n");
// `String::from` because despite `doc_comment` being a `String`, the macro still ends up
// with a `&str` somehow.
tokens.extend(quote!( (Self::#ident, String::from(#doc_comment) ) ))
}
}

16
conduit-macros/src/lib.rs

@ -0,0 +1,16 @@
use proc_macro::TokenStream;
use quote::quote;
#[cfg(feature = "doc-generators")]
mod doc_generators;
#[cfg(feature = "doc-generators")]
#[proc_macro_derive(DocumentRestrictions)]
pub fn document_restrictions(item: TokenStream) -> TokenStream {
use doc_generators::Restrictions;
use syn::parse_macro_input;
let restrictions = parse_macro_input!(item as Restrictions);
quote! { #restrictions }.into()
}

2
conduit/src/service/rate_limiting/mod.rs

@ -130,8 +130,6 @@ impl Service {
})
}
//TODO: use checked and saturating arithmetic
/// Takes the target and request, and either accepts the request while adding to the
/// bucket, or rejects the request, returning the duration that should be waited until
/// the request should be retried.

19
docs/configuration/rate-limiting.md

@ -34,11 +34,7 @@ should be part of the preset, you can contribute and change them!
The overrides are split into `client` and `federation` sections, for limits that apply to the
[client](https://spec.matrix.org/v1.17/client-server-api/) and
[federation](https://spec.matrix.org/v1.17/server-server-api/) APIs respectively, which are both
then split into `target` and `global` sections, which apply to specific [targets](#targets) and globally respectively.
### Restrictions
{{#include ../../target/docs/rate-limiting.md:restrictions}}
then split into `target` and `global` sections, which apply to singular [targets](#targets) or to all of them respectively.
### Targets
@ -53,3 +49,16 @@ resources/requests each unique client can access within the configured timeframe
For example, while on a small server you might allow for all logged-in users to send out 100 invites
per day between them, you can set a cap of 5 for each individual user, not only so that they can't
use up the entire global cap, but also prevent potential spam from being spread by that user alone.
### Restrictions
Restrictions are one-to-many mappings to endpoints that have potential for abuse. Like the overrides mentioned above,
they are split into `client` and `federation` restrictions.
#### Client
{{#include ../../target/docs/rate-limiting.md:client-restrictions}}
#### Federation
{{#include ../../target/docs/rate-limiting.md:federation-restrictions}}

1
flake.nix

@ -29,6 +29,7 @@
workspaceMembers = (pkgs.lib.importTOML ./Cargo.toml).workspace.members;
default = self.callPackage ./nix/pkgs/default {};
xtask = self.callPackage ./nix/pkgs/default { pname = "xtask"; };
inherit inputs;

4
nix/pkgs/book/default.nix

@ -1,8 +1,10 @@
# Keep sorted
{ default
, inputs
, lib
, mdbook
, stdenv
, xtask
}:
stdenv.mkDerivation {
@ -20,6 +22,7 @@ stdenv.mkDerivation {
"debian/README.md"
"docs"
"README.md"
"target/docs"
];
};
@ -28,6 +31,7 @@ stdenv.mkDerivation {
];
buildPhase = ''
${lib.getExe xtask} generate-docs
mdbook build
mv public $out
'';

7
nix/pkgs/default/default.nix

@ -12,6 +12,7 @@
, default-features ? true
, features ? []
, profile ? "release"
, pname ? "conduit"
}:
let
@ -44,7 +45,7 @@ let
commonAttrs = {
inherit
(craneLib.crateNameFromCargoToml {
cargoToml = "${inputs.self}/conduit/Cargo.toml";
cargoToml = "${inputs.self}/${pname}/Cargo.toml";
})
pname
version;
@ -77,7 +78,7 @@ craneLib.buildPackage ( commonAttrs // {
env = buildDepsOnlyEnv;
});
cargoExtraArgs = "--locked "
cargoExtraArgs = "-p ${pname} --locked "
+ lib.optionalString
(!default-features)
"--no-default-features "
@ -94,5 +95,5 @@ craneLib.buildPackage ( commonAttrs // {
env = buildPackageEnv;
};
meta.mainProgram = commonAttrs.pname;
meta.mainProgram = pname;
})

12
xtask/Cargo.toml

@ -0,0 +1,12 @@
[package]
edition.workspace = true
homepage.workspace = true
name = "xtask"
repository.workspace = true
rust-version.workspace = true
version = "0.11.0-alpha"
[dependencies]
[lints]
workspace = true

3
xtask/src/main.rs

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}
Loading…
Cancel
Save