mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-28 09:11:34 +00:00
feat: add rust-libp2p-server to monorepo
Moves https://github.com/mxinden/rust-libp2p-server to the rust-libp2p monorepository. > # Rust libp2p Server > > A rust-libp2p based server implementation running: > > - the [Kademlia protocol](https://github.com/libp2p/specs/tree/master/kad-dht) > > - the [Circuit Relay v2 protocol](https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md) > > - the [AutoNAT protocol](https://github.com/libp2p/specs/blob/master/autonat/README.md) Pull-Request: #4311.
This commit is contained in:
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -63,6 +63,7 @@ jobs:
|
|||||||
crate: ${{ env.CRATE }}
|
crate: ${{ env.CRATE }}
|
||||||
|
|
||||||
- name: Enforce no dependency on meta crate
|
- name: Enforce no dependency on meta crate
|
||||||
|
if: env.CRATE != 'libp2p-server'
|
||||||
run: |
|
run: |
|
||||||
cargo metadata --format-version=1 --no-deps | \
|
cargo metadata --format-version=1 --no-deps | \
|
||||||
jq -e -r '.packages[] | select(.name == "'"$CRATE"'") | .dependencies | all(.name != "libp2p")'
|
jq -e -r '.packages[] | select(.name == "'"$CRATE"'") | .dependencies | all(.name != "libp2p")'
|
||||||
|
35
.github/workflows/docker-image.yml
vendored
Normal file
35
.github/workflows/docker-image.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: Publish docker images
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
tags:
|
||||||
|
- 'libp2p-server-**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
server:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ github.token }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Get branch or tag name
|
||||||
|
id: ref-name
|
||||||
|
run: echo ::set-output name=ref::${GITHUB_REF#refs/*/}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v3
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./misc/server/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ghcr.io/${{ github.repository }}-server:${{ steps.ref-name.outputs.ref }}
|
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -3174,6 +3174,26 @@ dependencies = [
|
|||||||
"void",
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libp2p-server"
|
||||||
|
version = "0.12.1"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.2",
|
||||||
|
"clap",
|
||||||
|
"env_logger 0.10.0",
|
||||||
|
"futures",
|
||||||
|
"futures-timer",
|
||||||
|
"hyper",
|
||||||
|
"libp2p",
|
||||||
|
"log",
|
||||||
|
"prometheus-client",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libp2p-swarm"
|
name = "libp2p-swarm"
|
||||||
version = "0.43.3"
|
version = "0.43.3"
|
||||||
|
@ -24,6 +24,7 @@ members = [
|
|||||||
"misc/quick-protobuf-codec",
|
"misc/quick-protobuf-codec",
|
||||||
"misc/quickcheck-ext",
|
"misc/quickcheck-ext",
|
||||||
"misc/rw-stream-sink",
|
"misc/rw-stream-sink",
|
||||||
|
"misc/server",
|
||||||
"muxers/mplex",
|
"muxers/mplex",
|
||||||
"muxers/test-harness",
|
"muxers/test-harness",
|
||||||
"muxers/yamux",
|
"muxers/yamux",
|
||||||
@ -89,6 +90,7 @@ libp2p-quic = { version = "0.9.2", path = "transports/quic" }
|
|||||||
libp2p-relay = { version = "0.16.1", path = "protocols/relay" }
|
libp2p-relay = { version = "0.16.1", path = "protocols/relay" }
|
||||||
libp2p-rendezvous = { version = "0.13.0", path = "protocols/rendezvous" }
|
libp2p-rendezvous = { version = "0.13.0", path = "protocols/rendezvous" }
|
||||||
libp2p-request-response = { version = "0.25.1", path = "protocols/request-response" }
|
libp2p-request-response = { version = "0.25.1", path = "protocols/request-response" }
|
||||||
|
libp2p-server = { version = "0.12.1", path = "misc/server" }
|
||||||
libp2p-swarm = { version = "0.43.3", path = "swarm" }
|
libp2p-swarm = { version = "0.43.3", path = "swarm" }
|
||||||
libp2p-swarm-derive = { version = "0.33.0", path = "swarm-derive" }
|
libp2p-swarm-derive = { version = "0.33.0", path = "swarm-derive" }
|
||||||
libp2p-swarm-test = { version = "0.2.0", path = "swarm-test" }
|
libp2p-swarm-test = { version = "0.2.0", path = "swarm-test" }
|
||||||
|
53
misc/server/CHANGELOG.md
Normal file
53
misc/server/CHANGELOG.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.12.1] - unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Move to tokio and hyper.
|
||||||
|
See [PR 4311].
|
||||||
|
- Move to distroless Docker base image.
|
||||||
|
See [PR 4311].
|
||||||
|
|
||||||
|
[PR 4311]: https://github.com/libp2p/rust-libp2p/pull/4311
|
||||||
|
|
||||||
|
## [0.8.0]
|
||||||
|
### Changed
|
||||||
|
- Remove mplex support.
|
||||||
|
|
||||||
|
## [0.7.0]
|
||||||
|
### Changed
|
||||||
|
- Update to libp2p v0.47.0.
|
||||||
|
|
||||||
|
## [0.6.0] - [2022-05-05]
|
||||||
|
### Changed
|
||||||
|
- Update to libp2p v0.44.0.
|
||||||
|
|
||||||
|
## [0.5.4] - [2022-01-11]
|
||||||
|
### Changed
|
||||||
|
- Pull latest autonat changes.
|
||||||
|
|
||||||
|
## [0.5.3] - [2021-12-25]
|
||||||
|
### Changed
|
||||||
|
- Update dependencies.
|
||||||
|
- Pull in autonat fixes.
|
||||||
|
|
||||||
|
## [0.5.2] - [2021-12-20]
|
||||||
|
### Added
|
||||||
|
- Add support for libp2p autonat protocol via `--enable-autonat`.
|
||||||
|
|
||||||
|
## [0.5.1] - [2021-12-20]
|
||||||
|
### Fixed
|
||||||
|
- Update dependencies.
|
||||||
|
- Fix typo in command line flag `--enable-kademlia`.
|
||||||
|
|
||||||
|
## [0.5.0] - 2021-11-18
|
||||||
|
### Changed
|
||||||
|
- Disable Kademlia protocol by default.
|
||||||
|
|
||||||
|
## [0.4.0] - 2021-11-18
|
||||||
|
### Fixed
|
||||||
|
- Update dependencies.
|
26
misc/server/Cargo.toml
Normal file
26
misc/server/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "libp2p-server"
|
||||||
|
version = "0.12.1"
|
||||||
|
authors = ["Max Inden <mail@max-inden.de>"]
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = { workspace = true }
|
||||||
|
description = "A rust-libp2p server binary."
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
base64 = "0.21"
|
||||||
|
clap = { version = "4.3.12", features = ["derive"] }
|
||||||
|
env_logger = "0.10.0"
|
||||||
|
futures = "0.3"
|
||||||
|
futures-timer = "3"
|
||||||
|
hyper = { version = "0.14", features = ["server", "tcp", "http1"] }
|
||||||
|
libp2p = { path = "../../libp2p", features = ["autonat", "dns", "tokio", "noise", "tcp", "yamux", "identify", "kad", "ping", "relay", "metrics", "rsa", "macros", "quic"] }
|
||||||
|
log = "0.4"
|
||||||
|
prometheus-client = "0.21.2"
|
||||||
|
serde = "1.0.183"
|
||||||
|
serde_derive = "1.0.125"
|
||||||
|
serde_json = "1.0"
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||||
|
zeroize = "1"
|
16
misc/server/Dockerfile
Normal file
16
misc/server/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
FROM rust:1.66-bullseye as builder
|
||||||
|
WORKDIR /usr/src/rust-libp2p-server
|
||||||
|
|
||||||
|
# Run with access to the target cache to speed up builds
|
||||||
|
WORKDIR /workspace
|
||||||
|
ADD . .
|
||||||
|
RUN --mount=type=cache,target=./target \
|
||||||
|
--mount=type=cache,target=/usr/local/cargo/registry \
|
||||||
|
cargo build --release --package libp2p-server
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=./target \
|
||||||
|
mv ./target/release/libp2p-server /usr/local/bin/libp2p-server
|
||||||
|
|
||||||
|
FROM gcr.io/distroless/cc
|
||||||
|
COPY --from=builder /usr/local/bin/libp2p-server /usr/local/bin/libp2p-server
|
||||||
|
CMD ["libp2p-server"]
|
35
misc/server/README.md
Normal file
35
misc/server/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Rust libp2p Server
|
||||||
|
|
||||||
|
A rust-libp2p based server implementation running:
|
||||||
|
|
||||||
|
- the [Kademlia protocol](https://github.com/libp2p/specs/tree/master/kad-dht)
|
||||||
|
|
||||||
|
- the [Circuit Relay v2 protocol](https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md)
|
||||||
|
|
||||||
|
- the [AutoNAT protocol](https://github.com/libp2p/specs/blob/master/autonat/README.md)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo run -- --help
|
||||||
|
|
||||||
|
A rust-libp2p server binary.
|
||||||
|
|
||||||
|
Usage: libp2p-server [OPTIONS] --config <CONFIG>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--config <CONFIG> Path to IPFS config file
|
||||||
|
--metrics-path <METRICS_PATH> Metric endpoint path [default: /metrics]
|
||||||
|
--enable-kademlia Whether to run the libp2p Kademlia protocol and join the IPFS DHT
|
||||||
|
--enable-autonat Whether to run the libp2p Autonat protocol
|
||||||
|
-h, --help Print help
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo run -- --config ~/.ipfs/config
|
||||||
|
|
||||||
|
Local peer id: PeerId("12D3KooWSa1YEeQVSwvoqAMhwjKQ6kqZQckhWPb3RWEGV3sZGU6Z")
|
||||||
|
Listening on "/ip4/127.0.0.1/udp/4001/quic"
|
||||||
|
[...]
|
||||||
|
```
|
78
misc/server/src/behaviour.rs
Normal file
78
misc/server/src/behaviour.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use libp2p::autonat;
|
||||||
|
use libp2p::identify;
|
||||||
|
use libp2p::kad::{record::store::MemoryStore, Kademlia, KademliaConfig};
|
||||||
|
use libp2p::ping;
|
||||||
|
use libp2p::relay;
|
||||||
|
use libp2p::swarm::behaviour::toggle::Toggle;
|
||||||
|
use libp2p::{identity, swarm::NetworkBehaviour, Multiaddr, PeerId};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
const BOOTNODES: [&str; 4] = [
|
||||||
|
"QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
|
||||||
|
"QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa",
|
||||||
|
"QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb",
|
||||||
|
"QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt",
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(NetworkBehaviour)]
|
||||||
|
pub(crate) struct Behaviour {
|
||||||
|
relay: relay::Behaviour,
|
||||||
|
ping: ping::Behaviour,
|
||||||
|
identify: identify::Behaviour,
|
||||||
|
pub(crate) kademlia: Toggle<Kademlia<MemoryStore>>,
|
||||||
|
autonat: Toggle<autonat::Behaviour>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Behaviour {
|
||||||
|
pub(crate) fn new(
|
||||||
|
pub_key: identity::PublicKey,
|
||||||
|
enable_kademlia: bool,
|
||||||
|
enable_autonat: bool,
|
||||||
|
) -> Self {
|
||||||
|
let kademlia = if enable_kademlia {
|
||||||
|
let mut kademlia_config = KademliaConfig::default();
|
||||||
|
// Instantly remove records and provider records.
|
||||||
|
//
|
||||||
|
// TODO: Replace hack with option to disable both.
|
||||||
|
kademlia_config.set_record_ttl(Some(Duration::from_secs(0)));
|
||||||
|
kademlia_config.set_provider_record_ttl(Some(Duration::from_secs(0)));
|
||||||
|
let mut kademlia = Kademlia::with_config(
|
||||||
|
pub_key.to_peer_id(),
|
||||||
|
MemoryStore::new(pub_key.to_peer_id()),
|
||||||
|
kademlia_config,
|
||||||
|
);
|
||||||
|
let bootaddr = Multiaddr::from_str("/dnsaddr/bootstrap.libp2p.io").unwrap();
|
||||||
|
for peer in &BOOTNODES {
|
||||||
|
kademlia.add_address(&PeerId::from_str(peer).unwrap(), bootaddr.clone());
|
||||||
|
}
|
||||||
|
kademlia.bootstrap().unwrap();
|
||||||
|
Some(kademlia)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let autonat = if enable_autonat {
|
||||||
|
Some(autonat::Behaviour::new(
|
||||||
|
PeerId::from(pub_key.clone()),
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
relay: relay::Behaviour::new(PeerId::from(pub_key.clone()), Default::default()),
|
||||||
|
ping: ping::Behaviour::new(ping::Config::new()),
|
||||||
|
identify: identify::Behaviour::new(
|
||||||
|
identify::Config::new("ipfs/0.1.0".to_string(), pub_key).with_agent_version(
|
||||||
|
format!("rust-libp2p-server/{}", env!("CARGO_PKG_VERSION")),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
kademlia,
|
||||||
|
autonat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
misc/server/src/config.rs
Normal file
39
misc/server/src/config.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use libp2p::Multiaddr;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub(crate) struct Config {
|
||||||
|
pub(crate) identity: Identity,
|
||||||
|
pub(crate) addresses: Addresses,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub(crate) fn from_file(path: &Path) -> Result<Self, Box<dyn Error>> {
|
||||||
|
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub(crate) struct Identity {
|
||||||
|
#[serde(rename = "PeerID")]
|
||||||
|
pub(crate) peer_id: String,
|
||||||
|
pub(crate) priv_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub(crate) struct Addresses {
|
||||||
|
pub(crate) swarm: Vec<Multiaddr>,
|
||||||
|
pub(crate) append_announce: Vec<Multiaddr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl zeroize::Zeroize for Config {
|
||||||
|
fn zeroize(&mut self) {
|
||||||
|
self.identity.peer_id.zeroize();
|
||||||
|
self.identity.priv_key.zeroize();
|
||||||
|
}
|
||||||
|
}
|
131
misc/server/src/http_service.rs
Normal file
131
misc/server/src/http_service.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Copyright 2022 Protocol Labs.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
use hyper::http::StatusCode;
|
||||||
|
use hyper::service::Service;
|
||||||
|
use hyper::{Body, Method, Request, Response, Server};
|
||||||
|
use log::{error, info};
|
||||||
|
use prometheus_client::encoding::text::encode;
|
||||||
|
use prometheus_client::registry::Registry;
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0";
|
||||||
|
|
||||||
|
pub(crate) async fn metrics_server(registry: Registry) -> Result<(), std::io::Error> {
|
||||||
|
// Serve on localhost.
|
||||||
|
let addr = ([127, 0, 0, 1], 8080).into();
|
||||||
|
|
||||||
|
// Use the tokio runtime to run the hyper server.
|
||||||
|
let rt = tokio::runtime::Runtime::new()?;
|
||||||
|
rt.block_on(async {
|
||||||
|
let server = Server::bind(&addr).serve(MakeMetricService::new(registry));
|
||||||
|
info!("Metrics server on http://{}/metrics", server.local_addr());
|
||||||
|
if let Err(e) = server.await {
|
||||||
|
error!("server error: {}", e);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct MetricService {
|
||||||
|
reg: Arc<Mutex<Registry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharedRegistry = Arc<Mutex<Registry>>;
|
||||||
|
|
||||||
|
impl MetricService {
|
||||||
|
fn get_reg(&mut self) -> SharedRegistry {
|
||||||
|
Arc::clone(&self.reg)
|
||||||
|
}
|
||||||
|
fn respond_with_metrics(&mut self) -> Response<String> {
|
||||||
|
let mut response: Response<String> = Response::default();
|
||||||
|
|
||||||
|
response.headers_mut().insert(
|
||||||
|
hyper::header::CONTENT_TYPE,
|
||||||
|
METRICS_CONTENT_TYPE.try_into().unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let reg = self.get_reg();
|
||||||
|
encode(&mut response.body_mut(), ®.lock().unwrap()).unwrap();
|
||||||
|
|
||||||
|
*response.status_mut() = StatusCode::OK;
|
||||||
|
|
||||||
|
response
|
||||||
|
}
|
||||||
|
fn respond_with_404_not_found(&mut self) -> Response<String> {
|
||||||
|
Response::builder()
|
||||||
|
.status(StatusCode::NOT_FOUND)
|
||||||
|
.body("Not found try localhost:[port]/metrics".to_string())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service<Request<Body>> for MetricService {
|
||||||
|
type Response = Response<String>;
|
||||||
|
type Error = hyper::Error;
|
||||||
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||||
|
let req_path = req.uri().path();
|
||||||
|
let req_method = req.method();
|
||||||
|
let resp = if (req_method == Method::GET) && (req_path == "/metrics") {
|
||||||
|
// Encode and serve metrics from registry.
|
||||||
|
self.respond_with_metrics()
|
||||||
|
} else {
|
||||||
|
self.respond_with_404_not_found()
|
||||||
|
};
|
||||||
|
Box::pin(async { Ok(resp) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct MakeMetricService {
|
||||||
|
reg: SharedRegistry,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MakeMetricService {
|
||||||
|
pub(crate) fn new(registry: Registry) -> MakeMetricService {
|
||||||
|
MakeMetricService {
|
||||||
|
reg: Arc::new(Mutex::new(registry)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Service<T> for MakeMetricService {
|
||||||
|
type Response = MetricService;
|
||||||
|
type Error = hyper::Error;
|
||||||
|
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, _: T) -> Self::Future {
|
||||||
|
let reg = self.reg.clone();
|
||||||
|
let fut = async move { Ok(MetricService { reg }) };
|
||||||
|
Box::pin(fut)
|
||||||
|
}
|
||||||
|
}
|
213
misc/server/src/main.rs
Normal file
213
misc/server/src/main.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
use base64::Engine;
|
||||||
|
use clap::Parser;
|
||||||
|
use futures::executor::block_on;
|
||||||
|
use futures::future::Either;
|
||||||
|
use futures::stream::StreamExt;
|
||||||
|
use futures_timer::Delay;
|
||||||
|
use libp2p::core::muxing::StreamMuxerBox;
|
||||||
|
use libp2p::core::upgrade;
|
||||||
|
use libp2p::dns;
|
||||||
|
use libp2p::identify;
|
||||||
|
use libp2p::identity;
|
||||||
|
use libp2p::identity::PeerId;
|
||||||
|
use libp2p::kad;
|
||||||
|
use libp2p::metrics::{Metrics, Recorder};
|
||||||
|
use libp2p::noise;
|
||||||
|
use libp2p::quic;
|
||||||
|
use libp2p::swarm::{SwarmBuilder, SwarmEvent};
|
||||||
|
use libp2p::tcp;
|
||||||
|
use libp2p::yamux;
|
||||||
|
use libp2p::Transport;
|
||||||
|
use log::{debug, info};
|
||||||
|
use prometheus_client::metrics::info::Info;
|
||||||
|
use prometheus_client::registry::Registry;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::task::Poll;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
mod behaviour;
|
||||||
|
mod config;
|
||||||
|
mod http_service;
|
||||||
|
|
||||||
|
const BOOTSTRAP_INTERVAL: Duration = Duration::from_secs(5 * 60);
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
#[clap(name = "libp2p server", about = "A rust-libp2p server binary.")]
|
||||||
|
struct Opts {
|
||||||
|
/// Path to IPFS config file.
|
||||||
|
#[clap(long)]
|
||||||
|
config: PathBuf,
|
||||||
|
|
||||||
|
/// Metric endpoint path.
|
||||||
|
#[clap(long, default_value = "/metrics")]
|
||||||
|
metrics_path: String,
|
||||||
|
|
||||||
|
/// Whether to run the libp2p Kademlia protocol and join the IPFS DHT.
|
||||||
|
#[clap(long)]
|
||||||
|
enable_kademlia: bool,
|
||||||
|
|
||||||
|
/// Whether to run the libp2p Autonat protocol.
|
||||||
|
#[clap(long)]
|
||||||
|
enable_autonat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let opt = Opts::parse();
|
||||||
|
|
||||||
|
let config = Zeroizing::new(config::Config::from_file(opt.config.as_path())?);
|
||||||
|
|
||||||
|
let (local_peer_id, local_keypair) = {
|
||||||
|
let keypair = identity::Keypair::from_protobuf_encoding(&Zeroizing::new(
|
||||||
|
base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(config.identity.priv_key.as_bytes())?,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let peer_id = keypair.public().into();
|
||||||
|
assert_eq!(
|
||||||
|
PeerId::from_str(&config.identity.peer_id)?,
|
||||||
|
peer_id,
|
||||||
|
"Expect peer id derived from private key and peer id retrieved from config to match."
|
||||||
|
);
|
||||||
|
|
||||||
|
(peer_id, keypair)
|
||||||
|
};
|
||||||
|
println!("Local peer id: {local_peer_id}");
|
||||||
|
|
||||||
|
let transport = {
|
||||||
|
let tcp_transport =
|
||||||
|
tcp::tokio::Transport::new(tcp::Config::new().port_reuse(true).nodelay(true))
|
||||||
|
.upgrade(upgrade::Version::V1)
|
||||||
|
.authenticate(noise::Config::new(&local_keypair)?)
|
||||||
|
.multiplex(yamux::Config::default())
|
||||||
|
.timeout(Duration::from_secs(20));
|
||||||
|
|
||||||
|
let quic_transport = {
|
||||||
|
let mut config = quic::Config::new(&local_keypair);
|
||||||
|
config.support_draft_29 = true;
|
||||||
|
quic::tokio::Transport::new(config)
|
||||||
|
};
|
||||||
|
|
||||||
|
dns::TokioDnsConfig::system(libp2p::core::transport::OrTransport::new(
|
||||||
|
quic_transport,
|
||||||
|
tcp_transport,
|
||||||
|
))?
|
||||||
|
.map(|either_output, _| match either_output {
|
||||||
|
Either::Left((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
|
||||||
|
Either::Right((peer_id, muxer)) => (peer_id, StreamMuxerBox::new(muxer)),
|
||||||
|
})
|
||||||
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||||
|
.boxed()
|
||||||
|
};
|
||||||
|
|
||||||
|
let behaviour = behaviour::Behaviour::new(
|
||||||
|
local_keypair.public(),
|
||||||
|
opt.enable_kademlia,
|
||||||
|
opt.enable_autonat,
|
||||||
|
);
|
||||||
|
let mut swarm = SwarmBuilder::with_tokio_executor(transport, behaviour, local_peer_id).build();
|
||||||
|
|
||||||
|
if config.addresses.swarm.is_empty() {
|
||||||
|
log::warn!("No listen addresses configured.");
|
||||||
|
}
|
||||||
|
for address in &config.addresses.swarm {
|
||||||
|
match swarm.listen_on(address.clone()) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e @ libp2p::TransportError::MultiaddrNotSupported(_)) => {
|
||||||
|
log::warn!("Failed to listen on {address}, continuing anyways, {e}")
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.addresses.append_announce.is_empty() {
|
||||||
|
log::warn!("No external addresses configured.");
|
||||||
|
}
|
||||||
|
for address in &config.addresses.append_announce {
|
||||||
|
swarm.add_external_address(address.clone())
|
||||||
|
}
|
||||||
|
log::info!(
|
||||||
|
"External addresses: {:?}",
|
||||||
|
swarm.external_addresses().collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut metric_registry = Registry::default();
|
||||||
|
let metrics = Metrics::new(&mut metric_registry);
|
||||||
|
let build_info = Info::new(vec![("version".to_string(), env!("CARGO_PKG_VERSION"))]);
|
||||||
|
metric_registry.register(
|
||||||
|
"build",
|
||||||
|
"A metric with a constant '1' value labeled by version",
|
||||||
|
build_info,
|
||||||
|
);
|
||||||
|
thread::spawn(move || block_on(http_service::metrics_server(metric_registry)));
|
||||||
|
|
||||||
|
let mut bootstrap_timer = Delay::new(BOOTSTRAP_INTERVAL);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Poll::Ready(()) = futures::poll!(&mut bootstrap_timer) {
|
||||||
|
bootstrap_timer.reset(BOOTSTRAP_INTERVAL);
|
||||||
|
let _ = swarm
|
||||||
|
.behaviour_mut()
|
||||||
|
.kademlia
|
||||||
|
.as_mut()
|
||||||
|
.map(|k| k.bootstrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let event = swarm.next().await.expect("Swarm not to terminate.");
|
||||||
|
metrics.record(&event);
|
||||||
|
match event {
|
||||||
|
SwarmEvent::Behaviour(behaviour::BehaviourEvent::Identify(e)) => {
|
||||||
|
info!("{:?}", e);
|
||||||
|
metrics.record(&e);
|
||||||
|
|
||||||
|
if let identify::Event::Received {
|
||||||
|
peer_id,
|
||||||
|
info:
|
||||||
|
identify::Info {
|
||||||
|
listen_addrs,
|
||||||
|
protocols,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
} = e
|
||||||
|
{
|
||||||
|
if protocols.iter().any(|p| *p == kad::PROTOCOL_NAME) {
|
||||||
|
for addr in listen_addrs {
|
||||||
|
swarm
|
||||||
|
.behaviour_mut()
|
||||||
|
.kademlia
|
||||||
|
.as_mut()
|
||||||
|
.map(|k| k.add_address(&peer_id, addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwarmEvent::Behaviour(behaviour::BehaviourEvent::Ping(e)) => {
|
||||||
|
debug!("{:?}", e);
|
||||||
|
metrics.record(&e);
|
||||||
|
}
|
||||||
|
SwarmEvent::Behaviour(behaviour::BehaviourEvent::Kademlia(e)) => {
|
||||||
|
debug!("{:?}", e);
|
||||||
|
metrics.record(&e);
|
||||||
|
}
|
||||||
|
SwarmEvent::Behaviour(behaviour::BehaviourEvent::Relay(e)) => {
|
||||||
|
info!("{:?}", e);
|
||||||
|
metrics.record(&e)
|
||||||
|
}
|
||||||
|
SwarmEvent::Behaviour(behaviour::BehaviourEvent::Autonat(e)) => {
|
||||||
|
info!("{:?}", e);
|
||||||
|
// TODO: Add metric recording for `NatStatus`.
|
||||||
|
// metrics.record(&e)
|
||||||
|
}
|
||||||
|
SwarmEvent::NewListenAddr { address, .. } => {
|
||||||
|
println!("Listening on {address:?}");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user