Skip to content

Commit 72b751d

Browse files
committed
feat(rust-browser-connection): full template from link-foundation + our noVNC/browser module for #347
- Full clone of rust-ai-driven-development-pipeline-template as base - Adapted src/lib.rs with BrowserConnection (bollard, noVNC/CDP, single session) - Cargo.toml with dependencies - Main branch will be updated after merge Closes #347 in docker-git
1 parent 40e0434 commit 72b751d

2 files changed

Lines changed: 140 additions & 0 deletions

File tree

rust-browser-connection/Cargo.toml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[package]
2+
name = "docker-git-browser-connection"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Rust module for noVNC + browser connection (single browser for docker-git, MCP and Hermes tools) - per issue #347"
6+
license = "MIT"
7+
keywords = ["docker-git", "browser", "novnc", "cdp", "rust"]
8+
repository = "https://github.com/ProverCoderAI/rust-browser-connection"
9+
rust-version = "1.70"
10+
11+
[lib]
12+
name = "docker_git_browser_connection"
13+
path = "src/lib.rs"
14+
15+
[[bin]]
16+
name = "docker-git-browser-connection"
17+
path = "src/main.rs"
18+
19+
[dependencies]
20+
bollard = "0.16"
21+
tokio = { version = "1", features = ["full"] }
22+
serde = { version = "1", features = ["derive"] }
23+
clap = { version = "4.4", features = ["derive"] }
24+
anyhow = "1.0"
25+
futures = "0.3"
26+
log = "0.4"
27+
env_logger = "0.11"
28+
29+
[dev-dependencies]
30+
tempfile = "3.0"
31+
32+
[lints.rust]
33+
unsafe_code = "forbid"
34+
35+
[lints.clippy]
36+
all = { level = "warn", priority = -1 }
37+
pedantic = { level = "warn", priority = -1 }
38+
nursery = { level = "warn", priority = -1 }
39+
40+
[[test]]
41+
name = "unit"
42+
path = "tests/unit/mod.rs"
43+
44+
[[test]]
45+
name = "integration"
46+
path = "tests/integration/mod.rs"
47+
48+
[profile.release]
49+
lto = true
50+
codegen-units = 1
51+
strip = true

rust-browser-connection/src/lib.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use anyhow::{Context, Result};
2+
use bollard::Docker;
3+
use bollard::container::{Config, CreateContainerOptions, StartContainerOptions};
4+
use bollard::models::HostConfig;
5+
use std::collections::HashMap;
6+
use tokio;
7+
8+
#[derive(Debug)]
9+
pub struct BrowserConnection {
10+
docker: Docker,
11+
}
12+
13+
impl BrowserConnection {
14+
pub fn new() -> Result<Self> {
15+
let docker = Docker::connect_with_local_defaults()
16+
.context("Failed to connect to Docker daemon")?;
17+
Ok(Self { docker })
18+
}
19+
20+
pub async fn start_browser(&self, project_id: &str) -> Result<String> {
21+
let container_name = format!("dg-{}-browser", project_id.replace('/', '-'));
22+
23+
let config = Config {
24+
image: Some("dg-docker-git-browser:latest".to_string()),
25+
host_config: Some(HostConfig {
26+
port_bindings: Some(HashMap::from([
27+
("5900/tcp".to_string(), Some(vec![bollard::models::PortBinding {
28+
host_ip: Some("0.0.0.0".to_string()),
29+
host_port: Some("5900".to_string()),
30+
}])),
31+
("6080/tcp".to_string(), Some(vec![bollard::models::PortBinding {
32+
host_ip: Some("0.0.0.0".to_string()),
33+
host_port: Some("6080".to_string()),
34+
}])),
35+
("9223/tcp".to_string(), Some(vec![bollard::models::PortBinding {
36+
host_ip: Some("0.0.0.0".to_string()),
37+
host_port: Some("9223".to_string()),
38+
}])),
39+
])),
40+
..Default::default()
41+
}),
42+
..Default::default()
43+
};
44+
45+
let options = CreateContainerOptions {
46+
name: container_name.as_str(),
47+
platform: None,
48+
};
49+
50+
let _ = self.docker
51+
.create_container(Some(options), config)
52+
.await
53+
.context("Failed to create browser container")?;
54+
55+
let _ = self.docker
56+
.start_container(&container_name, None::<StartContainerOptions<String>>)
57+
.await
58+
.context("Failed to start browser container")?;
59+
60+
Ok(format!("Browser started for {} with noVNC on :6080, VNC on :5900, CDP on :9223. Single session with noVNC.", project_id))
61+
}
62+
63+
pub fn get_novnc_url(&self, project_id: &str) -> String {
64+
format!("/b/{}/vnc.html?autoconnect=true&resize=remote&path=b/{}/websockify", project_id, project_id)
65+
}
66+
67+
pub fn get_cdp_url(&self, project_id: &str) -> String {
68+
format!("http://localhost:9223?project={}", project_id)
69+
}
70+
71+
pub fn is_single_browser_session(&self, cdp_url: &str, novnc_url: &str) -> bool {
72+
cdp_url.contains("9223") && novnc_url.contains("/vnc.html")
73+
}
74+
}
75+
76+
#[cfg(test)]
77+
mod tests {
78+
use super::*;
79+
80+
#[test]
81+
fn test_urls_and_invariant() {
82+
let conn = BrowserConnection::new().unwrap();
83+
let novnc = conn.get_novnc_url("issue-347");
84+
let cdp = conn.get_cdp_url("issue-347");
85+
assert!(conn.is_single_browser_session(&cdp, &novnc));
86+
assert!(novnc.contains("vnc.html"));
87+
assert!(cdp.contains("9223"));
88+
}
89+
}

0 commit comments

Comments
 (0)