Compare commits
17 commits
v0.4.0-bet
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
171e35d656 | ||
|
e624f88536 | ||
|
0835c0118e | ||
|
5dec9f6427 | ||
|
c4d97f378f | ||
|
2f5cdc93f7 | ||
|
8ab8f74821 | ||
|
fe105531f6 | ||
|
a5c5877010 | ||
|
27c472c196 | ||
|
1a40bd0355 | ||
|
7a5787e3ca | ||
|
b089972fd8 | ||
|
9f214fbbff | ||
|
b42225d3e5 | ||
|
35e7220b1d | ||
|
5849e8ae5e |
311
.drone.yml
Normal file
311
.drone.yml
Normal file
|
@ -0,0 +1,311 @@
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: clippy
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: clippy
|
||||||
|
image: asonix/rust-builder:latest-linux-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- rustup component add clippy
|
||||||
|
- cargo clippy -- -D warnings
|
||||||
|
- cargo clippy --no-default-features -- -D warnings
|
||||||
|
- cargo clippy --example fetch -- -D warnings
|
||||||
|
- cargo clippy --example resolver -- -D warnings
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: tests
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: tests
|
||||||
|
image: asonix/rust-builder:latest-linux-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo test
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: check-amd64
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo check --target=$TARGET
|
||||||
|
- cargo check --target=$TARGET --no-default-features
|
||||||
|
- cargo check --target=$TARGET --example fetch
|
||||||
|
- cargo check --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: check-arm64v8
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-arm64v8
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo check --target=$TARGET
|
||||||
|
- cargo check --target=$TARGET --no-default-features
|
||||||
|
- cargo check --target=$TARGET --example fetch
|
||||||
|
- cargo check --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: check-arm32v7
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-arm32v7
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo check --target=$TARGET
|
||||||
|
- cargo check --target=$TARGET --no-default-features
|
||||||
|
- cargo check --target=$TARGET --example fetch
|
||||||
|
- cargo check --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: build-amd64
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-amd64
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo build --target=$TARGET
|
||||||
|
- cargo build --target=$TARGET --example fetch
|
||||||
|
- cargo build --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: build-arm64v8
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-arm64v8
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo build --target=$TARGET
|
||||||
|
- cargo build --target=$TARGET --example fetch
|
||||||
|
- cargo build --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: build-arm32v7
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: asonix/rust-builder:latest-linux-arm32v7
|
||||||
|
pull: always
|
||||||
|
commands:
|
||||||
|
- cargo build --target=$TARGET
|
||||||
|
- cargo build --target=$TARGET --example fetch
|
||||||
|
- cargo build --target=$TARGET --example resolver
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: publish-crate
|
||||||
|
|
||||||
|
platform:
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
clone:
|
||||||
|
disable: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: clone
|
||||||
|
image: alpine/git:latest
|
||||||
|
user: root
|
||||||
|
commands:
|
||||||
|
- git clone $DRONE_GIT_HTTP_URL .
|
||||||
|
- git checkout $DRONE_COMMIT
|
||||||
|
- chown -R 991:991 .
|
||||||
|
|
||||||
|
- name: publish
|
||||||
|
image: asonix/rust-builder:latest-linux-amd64
|
||||||
|
pull: always
|
||||||
|
environment:
|
||||||
|
CRATES_IO_TOKEN:
|
||||||
|
from_secret: crates_io_token
|
||||||
|
commands:
|
||||||
|
- cargo publish --token $CRATES_IO_TOKEN
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- clippy
|
||||||
|
- tests
|
||||||
|
- build-amd64
|
||||||
|
- build-arm64v8
|
||||||
|
- build-arm32v7
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- tag
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
.direnv
|
||||||
|
.envrc
|
||||||
|
|
19
Cargo.toml
19
Cargo.toml
|
@ -1,22 +1,25 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-webfinger"
|
name = "actix-webfinger"
|
||||||
description = "Types and helpers to create and fetch Webfinger resources"
|
description = "Types and helpers to create and fetch Webfinger resources"
|
||||||
version = "0.4.0-beta.2"
|
version = "0.5.0"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
repository = "https://git.asonix.dog/Aardwolf/actix-webfinger"
|
repository = "https://git.asonix.dog/asonix/actix-webfinger"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["client"]
|
||||||
|
client = ["dep:awc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = "2.0.2"
|
actix-rt = "2.6.0"
|
||||||
actix-web = { version = "4.0.0-beta.3", default-features = false }
|
actix-web = { version = "4.0.1", default-features = false }
|
||||||
|
awc = { version = "3.0.0", default-features = false, optional = true }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.2"
|
pretty_env_logger = "0.5"
|
||||||
actix-web = { version = "4.0.0-beta.3", features = ["openssl"] }
|
|
||||||
pretty_env_logger = "0.4"
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
37
README.md
37
README.md
|
@ -13,23 +13,30 @@ First, add Actix Webfinger as a dependency
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = "=2.0.0-beta.1"
|
actix-rt = "2.6.0"
|
||||||
actix-web = "4.0.0-alpha.1"
|
actix-web = "4.0.0"
|
||||||
actix-webfinger = "0.4.0-beta.1"
|
actix-webfinger = "0.4.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
Then use it in your application
|
Then use it in your application
|
||||||
|
|
||||||
#### Client Example
|
#### Client Example
|
||||||
```rust
|
```rust
|
||||||
use actix_web::client::Client;
|
|
||||||
use actix_webfinger::Webfinger;
|
use actix_webfinger::Webfinger;
|
||||||
|
use awc::Client;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let client = Client::default();
|
let client = Client::default();
|
||||||
let wf = Webfinger::fetch(&client, "asonix@asonix.dog", "localhost:8000", false).await?;
|
let wf = Webfinger::fetch(
|
||||||
|
&client,
|
||||||
|
Some("acct:"),
|
||||||
|
"asonix@localhost:8000",
|
||||||
|
"localhost:8000",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
println!("asonix's webfinger:\n{:#?}", wf);
|
println!("asonix's webfinger:\n{:#?}", wf);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -38,7 +45,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
#### Server Example
|
#### Server Example
|
||||||
```rust
|
```rust
|
||||||
use actix_web::{web::Data, App, HttpServer};
|
use actix_web::{middleware::Logger, web::Data, App, HttpServer};
|
||||||
use actix_webfinger::{Resolver, Webfinger};
|
use actix_webfinger::{Resolver, Webfinger};
|
||||||
use std::{error::Error, future::Future, pin::Pin};
|
use std::{error::Error, future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -49,16 +56,19 @@ pub struct MyState {
|
||||||
|
|
||||||
pub struct MyResolver;
|
pub struct MyResolver;
|
||||||
|
|
||||||
|
type LocalBoxFuture<'a, Output> = Pin<Box<dyn Future<Output = Output> + 'a>>;
|
||||||
|
|
||||||
impl Resolver for MyResolver {
|
impl Resolver for MyResolver {
|
||||||
type State = Data<MyState>;
|
type State = Data<MyState>;
|
||||||
type Error = actix_web::error::JsonPayloadError;
|
type Error = actix_web::error::JsonPayloadError;
|
||||||
|
|
||||||
fn find(
|
fn find(
|
||||||
|
scheme: Option<&str>,
|
||||||
account: &str,
|
account: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
state: Data<MyState>,
|
state: Data<MyState>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
|
) -> LocalBoxFuture<'static, Result<Option<Webfinger>, Self::Error>> {
|
||||||
let w = if domain == state.domain {
|
let w = if scheme == Some("acct:") && domain == state.domain {
|
||||||
Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -70,12 +80,15 @@ impl Resolver for MyResolver {
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
std::env::set_var("RUST_LOG", "info");
|
||||||
|
pretty_env_logger::init();
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.data(MyState {
|
.app_data(Data::new(MyState {
|
||||||
domain: "asonix.dog".to_owned(),
|
domain: "localhost:8000".to_owned(),
|
||||||
})
|
}))
|
||||||
.service(actix_webfinger::resource::<_, MyResolver>())
|
.wrap(Logger::default())
|
||||||
|
.service(actix_webfinger::resource::<MyResolver>())
|
||||||
})
|
})
|
||||||
.bind("127.0.0.1:8000")?
|
.bind("127.0.0.1:8000")?
|
||||||
.run()
|
.run()
|
||||||
|
|
|
@ -1,11 +1,18 @@
|
||||||
use actix_web::client::Client;
|
|
||||||
use actix_webfinger::Webfinger;
|
use actix_webfinger::Webfinger;
|
||||||
|
use awc::Client;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let client = Client::default();
|
let client = Client::default();
|
||||||
let wf = Webfinger::fetch(&client, "asonix@localhost:8000", "localhost:8000", false).await?;
|
let wf = Webfinger::fetch(
|
||||||
|
&client,
|
||||||
|
Some("acct:"),
|
||||||
|
"asonix@localhost:8000",
|
||||||
|
"localhost:8000",
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
println!("asonix's webfinger:\n{:#?}", wf);
|
println!("asonix's webfinger:\n{:#?}", wf);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -9,16 +9,19 @@ pub struct MyState {
|
||||||
|
|
||||||
pub struct MyResolver;
|
pub struct MyResolver;
|
||||||
|
|
||||||
|
type LocalBoxFuture<'a, Output> = Pin<Box<dyn Future<Output = Output> + 'a>>;
|
||||||
|
|
||||||
impl Resolver for MyResolver {
|
impl Resolver for MyResolver {
|
||||||
type State = Data<MyState>;
|
type State = Data<MyState>;
|
||||||
type Error = actix_web::error::JsonPayloadError;
|
type Error = actix_web::error::JsonPayloadError;
|
||||||
|
|
||||||
fn find(
|
fn find(
|
||||||
|
scheme: Option<&str>,
|
||||||
account: &str,
|
account: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
state: Data<MyState>,
|
state: Data<MyState>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
|
) -> LocalBoxFuture<'static, Result<Option<Webfinger>, Self::Error>> {
|
||||||
let w = if domain == state.domain {
|
let w = if scheme == Some("acct:") && domain == state.domain {
|
||||||
Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -34,9 +37,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
pretty_env_logger::init();
|
pretty_env_logger::init();
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.data(MyState {
|
.app_data(Data::new(MyState {
|
||||||
domain: "localhost:8000".to_owned(),
|
domain: "localhost:8000".to_owned(),
|
||||||
})
|
}))
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.service(actix_webfinger::resource::<MyResolver>())
|
.service(actix_webfinger::resource::<MyResolver>())
|
||||||
})
|
})
|
||||||
|
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1689068808,
|
||||||
|
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1692174805,
|
||||||
|
"narHash": "sha256-xmNPFDi/AUMIxwgOH/IVom55Dks34u1g7sFKKebxUm0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "caac0eb6bdcad0b32cb2522e03e4002c8975c62e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
25
flake.nix
Normal file
25
flake.nix
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
description = "actix-webfinger";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.default = pkgs.hello;
|
||||||
|
|
||||||
|
devShell = with pkgs; mkShell {
|
||||||
|
nativeBuildInputs = [ cargo cargo-outdated cargo-zigbuild clippy gcc protobuf rust-analyzer rustc rustfmt ];
|
||||||
|
|
||||||
|
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
82
src/lib.rs
82
src/lib.rs
|
@ -13,23 +13,30 @@
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! actix = "0.10.0-alpha.1"
|
//! actix-rt = "2.6.0"
|
||||||
//! actix-web = "3.0.0-alpha.1"
|
//! actix-web = "4.0.0"
|
||||||
//! actix-webfinger = "0.3.0-alpha.2"
|
//! actix-webfinger = "0.4.0"
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Then use it in your application
|
//! Then use it in your application
|
||||||
//!
|
//!
|
||||||
//! #### Client Example
|
//! #### Client Example
|
||||||
//! ```rust,ignore
|
//! ```rust,ignore
|
||||||
//! use actix_web::client::Client;
|
|
||||||
//! use actix_webfinger::Webfinger;
|
//! use actix_webfinger::Webfinger;
|
||||||
|
//! use awc::Client;
|
||||||
//! use std::error::Error;
|
//! use std::error::Error;
|
||||||
//!
|
//!
|
||||||
//! #[actix_rt::main]
|
//! #[actix_rt::main]
|
||||||
//! async fn main() -> Result<(), Box<dyn Error>> {
|
//! async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
//! let client = Client::default();
|
//! let client = Client::default();
|
||||||
//! let wf = Webfinger::fetch(&client, "asonix@asonix.dog", "localhost:8000", false).await?;
|
//! let wf = Webfinger::fetch(
|
||||||
|
//! &client,
|
||||||
|
//! Some("acct:"),
|
||||||
|
//! "asonix@localhost:8000",
|
||||||
|
//! "localhost:8000",
|
||||||
|
//! false,
|
||||||
|
//! )
|
||||||
|
//! .await?;
|
||||||
//!
|
//!
|
||||||
//! println!("asonix's webfinger:\n{:#?}", wf);
|
//! println!("asonix's webfinger:\n{:#?}", wf);
|
||||||
//! Ok(())
|
//! Ok(())
|
||||||
|
@ -38,7 +45,7 @@
|
||||||
//!
|
//!
|
||||||
//! #### Server Example
|
//! #### Server Example
|
||||||
//! ```rust,ignore
|
//! ```rust,ignore
|
||||||
//! use actix_web::{web::Data, App, HttpServer};
|
//! use actix_web::{middleware::Logger, web::Data, App, HttpServer};
|
||||||
//! use actix_webfinger::{Resolver, Webfinger};
|
//! use actix_webfinger::{Resolver, Webfinger};
|
||||||
//! use std::{error::Error, future::Future, pin::Pin};
|
//! use std::{error::Error, future::Future, pin::Pin};
|
||||||
//!
|
//!
|
||||||
|
@ -49,16 +56,19 @@
|
||||||
//!
|
//!
|
||||||
//! pub struct MyResolver;
|
//! pub struct MyResolver;
|
||||||
//!
|
//!
|
||||||
|
//! type LocalBoxFuture<'a, Output> = Pin<Box<dyn Future<Output = Output> + 'a>>;
|
||||||
|
//!
|
||||||
//! impl Resolver for MyResolver {
|
//! impl Resolver for MyResolver {
|
||||||
//! type State = Data<MyState>;
|
//! type State = Data<MyState>;
|
||||||
//! type Error = actix_web::error::JsonPayloadError;
|
//! type Error = actix_web::error::JsonPayloadError;
|
||||||
//!
|
//!
|
||||||
//! fn find(
|
//! fn find(
|
||||||
|
//! scheme: Option<&str>,
|
||||||
//! account: &str,
|
//! account: &str,
|
||||||
//! domain: &str,
|
//! domain: &str,
|
||||||
//! state: S,
|
//! state: Data<MyState>,
|
||||||
//! ) -> Pin<Box<dyn Future<Output = Result<Option<Webfinger>, Self::Error>>>> {
|
//! ) -> LocalBoxFuture<'static, Result<Option<Webfinger>, Self::Error>> {
|
||||||
//! let w = if domain == state.domain {
|
//! let w = if scheme == Some("acct:") && domain == state.domain {
|
||||||
//! Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
//! Some(Webfinger::new(&format!("{}@{}", account, domain)))
|
||||||
//! } else {
|
//! } else {
|
||||||
//! None
|
//! None
|
||||||
|
@ -70,11 +80,14 @@
|
||||||
//!
|
//!
|
||||||
//! #[actix_rt::main]
|
//! #[actix_rt::main]
|
||||||
//! async fn main() -> Result<(), Box<dyn Error>> {
|
//! async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
//! std::env::set_var("RUST_LOG", "info");
|
||||||
|
//! pretty_env_logger::init();
|
||||||
//! HttpServer::new(|| {
|
//! HttpServer::new(|| {
|
||||||
//! App::new()
|
//! App::new()
|
||||||
//! .data(MyState {
|
//! .app_data(Data::new(MyState {
|
||||||
//! domain: "asonix.dog".to_owned(),
|
//! domain: "localhost:8000".to_owned(),
|
||||||
//! })
|
//! }))
|
||||||
|
//! .wrap(Logger::default())
|
||||||
//! .service(actix_webfinger::resource::<MyResolver>())
|
//! .service(actix_webfinger::resource::<MyResolver>())
|
||||||
//! })
|
//! })
|
||||||
//! .bind("127.0.0.1:8000")?
|
//! .bind("127.0.0.1:8000")?
|
||||||
|
@ -98,14 +111,14 @@
|
||||||
//!
|
//!
|
||||||
//! You should have received a copy of the GNU General Public License along with Actix Webfinger. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
|
//! You should have received a copy of the GNU General Public License along with Actix Webfinger. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
client::Client,
|
|
||||||
dev::RequestHead,
|
|
||||||
error::ResponseError,
|
error::ResponseError,
|
||||||
guard::Guard,
|
guard::{Guard, GuardContext},
|
||||||
http::Method,
|
http::Method,
|
||||||
web::{get, Query},
|
web::{get, Query},
|
||||||
FromRequest, HttpResponse, Resource,
|
FromRequest, HttpResponse, Resource,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "client")]
|
||||||
|
use awc::Client;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::{future::Future, pin::Pin};
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
@ -135,8 +148,8 @@ use std::{future::Future, pin::Pin};
|
||||||
pub struct WebfingerGuard;
|
pub struct WebfingerGuard;
|
||||||
|
|
||||||
impl Guard for WebfingerGuard {
|
impl Guard for WebfingerGuard {
|
||||||
fn check(&self, request: &RequestHead) -> bool {
|
fn check(&self, ctx: &GuardContext<'_>) -> bool {
|
||||||
let valid_accept = if let Some(val) = request.headers().get("Accept") {
|
let valid_accept = if let Some(val) = ctx.head().headers().get("Accept") {
|
||||||
if let Ok(s) = val.to_str() {
|
if let Ok(s) = val.to_str() {
|
||||||
s.split(',').any(|v| {
|
s.split(',').any(|v| {
|
||||||
let v = if let Some(index) = v.find(';') {
|
let v = if let Some(index) = v.find(';') {
|
||||||
|
@ -161,7 +174,7 @@ impl Guard for WebfingerGuard {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
valid_accept && request.method == Method::GET
|
valid_accept && ctx.head().method == Method::GET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,6 +262,7 @@ pub struct InvalidResource(String);
|
||||||
/// formatting before the request reaches the route handler.
|
/// formatting before the request reaches the route handler.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct WebfingerResource {
|
pub struct WebfingerResource {
|
||||||
|
pub scheme: Option<String>,
|
||||||
pub account: String,
|
pub account: String,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
}
|
}
|
||||||
|
@ -257,12 +271,24 @@ impl std::str::FromStr for WebfingerResource {
|
||||||
type Err = InvalidResource;
|
type Err = InvalidResource;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let trimmed = s.trim_start_matches("acct:").trim_start_matches('@');
|
let (scheme, trimmed) = s
|
||||||
|
.find(':')
|
||||||
|
.map(|index| {
|
||||||
|
let (scheme, trimmed) = s.split_at(index);
|
||||||
|
(
|
||||||
|
Some(scheme.to_owned() + ":"),
|
||||||
|
trimmed.trim_start_matches(':'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or((None, s));
|
||||||
|
|
||||||
|
let trimmed = trimmed.trim_start_matches('@');
|
||||||
|
|
||||||
if let Some(index) = trimmed.find('@') {
|
if let Some(index) = trimmed.find('@') {
|
||||||
let (account, domain) = trimmed.split_at(index);
|
let (account, domain) = trimmed.split_at(index);
|
||||||
|
|
||||||
Ok(WebfingerResource {
|
Ok(WebfingerResource {
|
||||||
|
scheme,
|
||||||
account: account.to_owned(),
|
account: account.to_owned(),
|
||||||
domain: domain.trim_start_matches('@').to_owned(),
|
domain: domain.trim_start_matches('@').to_owned(),
|
||||||
})
|
})
|
||||||
|
@ -353,6 +379,7 @@ pub trait Resolver {
|
||||||
type Error: ResponseError + 'static;
|
type Error: ResponseError + 'static;
|
||||||
|
|
||||||
fn find(
|
fn find(
|
||||||
|
scheme: Option<&str>,
|
||||||
account: &str,
|
account: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
state: Self::State,
|
state: Self::State,
|
||||||
|
@ -366,10 +393,14 @@ pub fn endpoint<R>(
|
||||||
where
|
where
|
||||||
R: Resolver,
|
R: Resolver,
|
||||||
{
|
{
|
||||||
let WebfingerResource { account, domain } = query.into_inner().resource;
|
let WebfingerResource {
|
||||||
|
scheme,
|
||||||
|
account,
|
||||||
|
domain,
|
||||||
|
} = query.into_inner().resource;
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
match R::find(&account, &domain, state).await? {
|
match R::find(scheme.as_deref(), &account, &domain, state).await? {
|
||||||
Some(w) => Ok(w.respond()),
|
Some(w) => Ok(w.respond()),
|
||||||
None => Ok(HttpResponse::NotFound().finish()),
|
None => Ok(HttpResponse::NotFound().finish()),
|
||||||
}
|
}
|
||||||
|
@ -605,20 +636,23 @@ impl Webfinger {
|
||||||
.json(&self)
|
.json(&self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
/// Fetch a webfinger with subject `user` from a given `domain`
|
/// Fetch a webfinger with subject `user` from a given `domain`
|
||||||
///
|
///
|
||||||
/// This method takes a `Client` so derivative works can provide their own configured clients
|
/// This method takes a `Client` so derivative works can provide their own configured clients
|
||||||
/// rather this library generating it's own http clients.
|
/// rather this library generating it's own http clients.
|
||||||
pub async fn fetch(
|
pub async fn fetch(
|
||||||
client: &Client,
|
client: &Client,
|
||||||
|
scheme: Option<&str>,
|
||||||
user: &str,
|
user: &str,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
https: bool,
|
https: bool,
|
||||||
) -> Result<Self, FetchError> {
|
) -> Result<Self, FetchError> {
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{}://{}/.well-known/webfinger?resource=acct:{}",
|
"{}://{}/.well-known/webfinger?resource={}{}",
|
||||||
if https { "https" } else { "http" },
|
if https { "https" } else { "http" },
|
||||||
domain,
|
domain,
|
||||||
|
scheme.unwrap_or("acct:"),
|
||||||
user
|
user
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -645,9 +679,9 @@ pub enum FetchError {
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{Webfinger, WebfingerQuery};
|
use crate::{Webfinger, WebfingerQuery};
|
||||||
|
|
||||||
const SIR_BOOPS: &'static str = r#"{"subject":"acct:Sir_Boops@sergal.org","aliases":["https://mastodon.sergal.org/@Sir_Boops","https://mastodon.sergal.org/users/Sir_Boops"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.sergal.org/@Sir_Boops"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.sergal.org/users/Sir_Boops.atom"},{"rel":"self","type":"application/activity+json","href":"https://mastodon.sergal.org/users/Sir_Boops"},{"rel":"salmon","href":"https://mastodon.sergal.org/api/salmon/1"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.sergal.org/authorize_interaction?uri={uri}"}]}"#;
|
const SIR_BOOPS: &str = r#"{"subject":"acct:Sir_Boops@sergal.org","aliases":["https://mastodon.sergal.org/@Sir_Boops","https://mastodon.sergal.org/users/Sir_Boops"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://mastodon.sergal.org/@Sir_Boops"},{"rel":"http://schemas.google.com/g/2010#updates-from","type":"application/atom+xml","href":"https://mastodon.sergal.org/users/Sir_Boops.atom"},{"rel":"self","type":"application/activity+json","href":"https://mastodon.sergal.org/users/Sir_Boops"},{"rel":"salmon","href":"https://mastodon.sergal.org/api/salmon/1"},{"rel":"magic-public-key","href":"data:application/magic-public-key,RSA.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB"},{"rel":"http://ostatus.org/schema/1.0/subscribe","template":"https://mastodon.sergal.org/authorize_interaction?uri={uri}"}]}"#;
|
||||||
|
|
||||||
const QUERIES: [&'static str; 4] = [
|
const QUERIES: [&str; 4] = [
|
||||||
r#"{"resource":"acct:asonix@asonix.dog"}"#,
|
r#"{"resource":"acct:asonix@asonix.dog"}"#,
|
||||||
r#"{"resource":"asonix@asonix.dog"}"#,
|
r#"{"resource":"asonix@asonix.dog"}"#,
|
||||||
r#"{"resource":"acct:@asonix@asonix.dog"}"#,
|
r#"{"resource":"acct:@asonix@asonix.dog"}"#,
|
||||||
|
|
Loading…
Reference in a new issue