feat(homeserver): add http relay in homeserver testnet for js testing

This commit is contained in:
nazeh
2024-12-17 18:00:52 +03:00
parent de7d34eca1
commit 992607a7a7
9 changed files with 98 additions and 56 deletions

6
Cargo.lock generated
View File

@@ -1247,6 +1247,7 @@ dependencies = [
"axum-server",
"futures-util",
"tokio",
"tracing",
"url",
]
@@ -1944,7 +1945,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkarr"
version = "3.0.0"
source = "git+https://github.com/Pubky/pkarr?branch=v3#aac62e74f3ab5d94b1a52cd00ff355d8d50e29e1"
source = "git+https://github.com/Pubky/pkarr?branch=v3#492639796bac0fedf42de7bbd3c069506b77fd19"
dependencies = [
"base32",
"byteorder",
@@ -1982,7 +1983,7 @@ dependencies = [
[[package]]
name = "pkarr-relay"
version = "0.1.0"
source = "git+https://github.com/Pubky/pkarr?branch=v3#aac62e74f3ab5d94b1a52cd00ff355d8d50e29e1"
source = "git+https://github.com/Pubky/pkarr?branch=v3#492639796bac0fedf42de7bbd3c069506b77fd19"
dependencies = [
"anyhow",
"axum",
@@ -2147,6 +2148,7 @@ dependencies = [
"futures-util",
"heed 0.21.0",
"hex",
"http-relay",
"httpdate",
"page_size",
"pkarr",

View File

@@ -9,4 +9,5 @@ axum = "0.7.9"
axum-server = "0.7.1"
futures-util = "0.3.31"
tokio = { version = "1.42.0", features = ["full"] }
tracing = "0.1.41"
url = "2.5.4"

View File

@@ -1,6 +1,6 @@
use std::{
collections::HashMap,
net::SocketAddr,
net::{SocketAddr, TcpListener},
sync::{Arc, Mutex},
};
@@ -16,18 +16,45 @@ use axum::{
use axum_server::Handle;
use tokio::sync::Notify;
use futures_util::stream::StreamExt;
use futures_util::{stream::StreamExt, TryFutureExt};
use url::Url;
// Shared state to store GET requests and their notifications
type SharedState = Arc<Mutex<HashMap<String, (Vec<u8>, Arc<Notify>)>>>;
#[derive(Debug, Default)]
pub struct Config {
pub http_port: u16,
}
#[derive(Debug, Default)]
pub struct HttpRelayBuilder(Config);
impl HttpRelayBuilder {
/// Configure the port used for HTTP server.
pub fn http_port(mut self, port: u16) -> Self {
self.0.http_port = port;
self
}
pub async fn build(self) -> Result<HttpRelay> {
HttpRelay::start(self.0).await
}
}
pub struct HttpRelay {
pub(crate) http_handle: Handle,
http_address: SocketAddr,
}
impl HttpRelay {
pub async fn start() -> Result<Self> {
pub fn builder() -> HttpRelayBuilder {
HttpRelayBuilder::default()
}
pub async fn start(config: Config) -> Result<Self> {
let shared_state: SharedState = Arc::new(Mutex::new(HashMap::new()));
let app = Router::new()
@@ -36,36 +63,35 @@ impl HttpRelay {
let http_handle = Handle::new();
let cloned = http_handle.clone();
tokio::spawn(async {
axum_server::bind(SocketAddr::from(([127, 0, 0, 1], 0)))
.handle(cloned)
.serve(app.into_make_service())
.await
.unwrap();
});
let http_listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], config.http_port)))?;
let http_address = http_listener.local_addr()?;
Ok(Self { http_handle })
tokio::spawn(
axum_server::from_tcp(http_listener)
.handle(http_handle.clone())
.serve(app.into_make_service())
.map_err(|error| tracing::error!(?error, "HttpRelay http server error")),
);
Ok(Self {
http_handle,
http_address,
})
}
pub async fn http_address(&self) -> Result<SocketAddr> {
match self.http_handle.listening().await {
Some(addr) => Ok(addr),
None => Err(anyhow::anyhow!("Failed to bind to http port")),
}
pub fn http_address(&self) -> SocketAddr {
self.http_address
}
/// Returns the localhost Url of this server.
pub async fn local_url(&self) -> Result<Url> {
match self.http_handle.listening().await {
Some(addr) => Ok(Url::parse(&format!("http://localhost:{}", addr.port()))?),
None => Err(anyhow::anyhow!("Failed to bind to http port")),
}
pub fn local_url(&self) -> Url {
Url::parse(&format!("http://localhost:{}", self.http_address.port()))
.expect("local_url should be formatted fine")
}
/// Returns the localhost URL of Link endpoints
pub async fn local_link_url(&self) -> Result<Url> {
let mut url = self.local_url().await?;
pub fn local_link_url(&self) -> Url {
let mut url = self.local_url();
let mut segments = url
.path_segments_mut()
@@ -75,7 +101,7 @@ impl HttpRelay {
drop(segments);
Ok(url)
url
}
pub fn shutdown(&self) {

View File

@@ -32,3 +32,4 @@ tower = "0.5.1"
page_size = "0.6.0"
pkarr-relay = { git = "https://github.com/Pubky/pkarr", branch = "v3", package = "pkarr-relay" }
http-relay = { version = "0.1.0", path = "../http-relay" }

View File

@@ -20,6 +20,9 @@ pub struct HttpServers {
pub(crate) http_handle: Handle,
/// Handle for the HTTPS server using Pkarr TLS
pub(crate) https_handle: Handle,
http_address: SocketAddr,
https_address: SocketAddr,
}
impl HttpServers {
@@ -27,6 +30,7 @@ impl HttpServers {
let http_listener =
// TODO: add config to http port
TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], 0)))?;
let http_address = http_listener.local_addr()?;
let http_handle = Handle::new();
@@ -43,6 +47,7 @@ impl HttpServers {
let https_listener =
TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], core.config().port)))?;
let https_address = https_listener.local_addr()?;
let https_handle = Handle::new();
@@ -63,21 +68,18 @@ impl HttpServers {
Ok(Self {
http_handle,
https_handle,
http_address,
https_address,
})
}
pub async fn http_address(&self) -> Result<SocketAddr> {
match self.http_handle.listening().await {
Some(addr) => Ok(addr),
None => Err(anyhow::anyhow!("Failed to bind to http port")),
}
pub fn http_address(&self) -> SocketAddr {
self.http_address
}
pub async fn https_address(&self) -> Result<SocketAddr> {
match self.https_handle.listening().await {
Some(addr) => Ok(addr),
None => Err(anyhow::anyhow!("Failed to bind to https port")),
}
pub fn https_address(&self) -> SocketAddr {
self.https_address
}
/// Shutdown all HTTP servers.

View File

@@ -78,7 +78,7 @@ impl Homeserver {
info!(
"Homeserver listening on http://localhost:{}",
http_servers.http_address().await?.port()
http_servers.http_address().port()
);
info!("Publishing Pkarr packet..");
@@ -86,9 +86,9 @@ impl Homeserver {
let pkarr_server = PkarrServer::new(
&config,
if config.testnet {
http_servers.http_address().await?.port()
http_servers.http_address().port()
} else {
http_servers.https_address().await?.port()
http_servers.https_address().port()
},
)?;
pkarr_server.publish_server_packet().await?;
@@ -104,6 +104,7 @@ impl Homeserver {
/// - Run a pkarr Relay on port `15411`
/// - Use a temporary storage for the both homeserver and relay
/// - Only publish http port (ignore https port or domain configurations)
/// - Run an HTTP relay on port `15412`
///
/// # Safety
/// See [Self::start]
@@ -113,19 +114,27 @@ impl Homeserver {
let storage =
std::env::temp_dir().join(pubky_common::timestamp::Timestamp::now().to_string());
let relay = unsafe {
let pkarr_relay = unsafe {
let mut config = pkarr_relay::Config {
http_port: 15411,
cache_path: Some(storage.join("pkarr-relay")),
rate_limiter: None,
..Default::default()
};
config.pkarr_config.dht_config.bootstrap = testnet.bootstrap.clone();
config.pkarr_config.resolvers = Some(vec![]);
pkarr_relay::Relay::start(config).await?
};
tracing::info!(relay_address=?relay.relay_address(), bootstrap=?relay.resolver_address(),"Running in Testnet mode");
let http_relay = http_relay::HttpRelay::builder()
.http_port(15412)
.build()
.await?;
tracing::info!(http_relay=?http_relay.local_link_url().as_str(), "Running http relay in Testnet mode");
tracing::info!(relay_address=?pkarr_relay.relay_address(), bootstrap=?pkarr_relay.resolver_address(),"Running pkarr relay in Testnet mode");
unsafe {
Homeserver::builder()

View File

@@ -2,7 +2,8 @@ import test from 'tape'
import { Client, Keypair, PublicKey } from '../index.cjs'
const Homeserver = PublicKey.from('8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo')
const HOMESERVER_PUBLICKEY = PublicKey.from('8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo')
const TESTNET_HTTP_RELAY = "http://localhost:15412/link";
// TODO: test multiple users in wasm
@@ -12,7 +13,7 @@ test('auth', async (t) => {
const keypair = Keypair.random()
const publicKey = keypair.publicKey()
await client.signup(keypair, Homeserver)
await client.signup(keypair, HOMESERVER_PUBLICKEY )
const session = await client.session(publicKey)
t.ok(session, "signup")
@@ -32,7 +33,7 @@ test('auth', async (t) => {
}
})
test.skip("3rd party signin", async (t) => {
test("3rd party signin", async (t) => {
let keypair = Keypair.random();
let pubky = keypair.publicKey().z32();
@@ -40,7 +41,7 @@ test.skip("3rd party signin", async (t) => {
let capabilities = "/pub/pubky.app/:rw,/pub/foo.bar/file:r";
let client = Client.testnet();
let [pubkyauth_url, pubkyauthResponse] = client
.authRequest("https://demo.httprelay.io/link", capabilities);
.authRequest(TESTNET_HTTP_RELAY, capabilities);
if (globalThis.document) {
// Skip `sendAuthToken` in browser
@@ -53,7 +54,7 @@ test.skip("3rd party signin", async (t) => {
{
let client = Client.testnet();
await client.signup(keypair, Homeserver);
await client.signup(keypair, HOMESERVER_PUBLICKEY);
await client.sendAuthToken(keypair, pubkyauth_url)
}

View File

@@ -2,14 +2,14 @@ import test from 'tape'
import { Client, Keypair, PublicKey } from '../index.cjs'
const Homeserver = PublicKey.from('8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo');
const HOMESERVER_PUBLICKEY = PublicKey.from('8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo')
test.skip('public: put/get', async (t) => {
const client = Client.testnet();
const keypair = Keypair.random();
await client.signup(keypair, Homeserver);
await client.signup(keypair, HOMESERVER_PUBLICKEY);
const publicKey = keypair.publicKey();
@@ -47,7 +47,7 @@ test.skip("not found", async (t) => {
const keypair = Keypair.random();
await client.signup(keypair, Homeserver);
await client.signup(keypair, HOMESERVER_PUBLICKEY);
const publicKey = keypair.publicKey();
@@ -64,7 +64,7 @@ test.skip("unauthorized", async (t) => {
const keypair = Keypair.random()
const publicKey = keypair.publicKey()
await client.signup(keypair, Homeserver)
await client.signup(keypair, HOMESERVER_PUBLICKEY)
const session = await client.session(publicKey)
t.ok(session, "signup")
@@ -91,7 +91,7 @@ test.skip("forbidden", async (t) => {
const keypair = Keypair.random()
const publicKey = keypair.publicKey()
await client.signup(keypair, Homeserver)
await client.signup(keypair, HOMESERVER_PUBLICKEY)
const session = await client.session(publicKey)
t.ok(session, "signup")
@@ -117,7 +117,7 @@ test.skip("list", async (t) => {
const publicKey = keypair.publicKey()
const pubky = publicKey.z32()
await client.signup(keypair, Homeserver)
await client.signup(keypair, HOMESERVER_PUBLICKEY)
@@ -248,7 +248,7 @@ test.skip('list shallow', async (t) => {
const publicKey = keypair.publicKey()
const pubky = publicKey.z32()
await client.signup(keypair, Homeserver)
await client.signup(keypair, HOMESERVER_PUBLICKEY)
let urls = [
`pubky://${pubky}/pub/a.com/a.txt`,

View File

@@ -271,8 +271,8 @@ mod tests {
let testnet = Testnet::new(10).unwrap();
let server = Homeserver::start_test(&testnet).await.unwrap();
let http_relay = HttpRelay::start().await.unwrap();
let http_relay_url = http_relay.local_link_url().await.unwrap();
let http_relay = HttpRelay::builder().build().await.unwrap();
let http_relay_url = http_relay.local_link_url();
let keypair = Keypair::random();
let pubky = keypair.public_key();