Summary
When configuring the Y-Sweet server with a url_prefix, the generated WebSocket URL and HTTP baseUrl are constructed inconsistently.
baseUrl correctly preserves the path prefix
url (WebSocket) drops the prefix, leading to incorrect connection URLs
Steps to Reproduce
-
Configure the Y-Sweet server with:
url_prefix = "https://example.domain.com/ysweet/"
-
Make a request to:
-
Observe the returned url and baseUrl fields in the response
Expected Behavior
Both URLs should preserve the /ysweet/ path prefix:
{
"url": "wss://example.domain.com/ysweet/d/{doc_id}/ws",
"baseUrl": "https://example.domain.com/ysweet/d/{doc_id}"
}
Actual Behavior
The WebSocket URL drops the path prefix, while the base URL is correct:
{
"url": "wss://example.domain.com/d/{doc_id}/ws",
"baseUrl": "https://example.domain.com/ysweet/d/{doc_id}"
}
Root Cause
The issue originates in the auth_doc function in
crates/y-sweet/src/server.rs (around lines 777–796).
WebSocket URL construction (problematic)
let url = if let Some(url_prefix) = &server_state.url_prefix {
let mut url = url_prefix.clone();
let scheme = if url.scheme() == "https" { "wss" } else { "ws" };
url.set_scheme(scheme).unwrap();
url = url.join(&format!("/d/{doc_id}/ws")).unwrap(); // ❌ Replaces entire path
url.to_string()
} else {
format!("ws://{host}/d/{doc_id}/ws")
};
url.join("/d/...") treats the argument as an absolute path
- This replaces the existing
/ysweet/ path entirely
Base URL construction (correct)
let base_url = if let Some(url_prefix) = &server_state.url_prefix {
let mut url_prefix = url_prefix.to_string();
if !url_prefix.ends_with('/') {
url_prefix = format!("{url_prefix}/");
}
format!("{url_prefix}d/{doc_id}") // ✅ Appends to existing path
} else {
format!("http://{host}/d/{doc_id}")
};
Suggested Fix
Construct the WebSocket URL using the same append-style approach as baseUrl instead of url.join():
let url = if let Some(url_prefix) = &server_state.url_prefix {
let mut url_prefix = url_prefix.to_string();
if !url_prefix.ends_with('/') {
url_prefix = format!("{url_prefix}/");
}
let scheme = if url_prefix.starts_with("https") { "wss" } else { "ws" };
let url_prefix = url_prefix
.replace("https://", "")
.replace("http://", "");
format!("{scheme}://{url_prefix}d/{doc_id}/ws")
} else {
format!("ws://{host}/d/{doc_id}/ws")
};
Impact
- WebSocket connections fail when Y-Sweet is deployed behind a reverse proxy or sub-path
- Breaks setups using path-based routing (e.g.,
/ysweet/)
I would be happy to open a PR or help with the fix
Summary
When configuring the Y-Sweet server with a
url_prefix, the generated WebSocket URL and HTTP baseUrl are constructed inconsistently.baseUrlcorrectly preserves the path prefixurl(WebSocket) drops the prefix, leading to incorrect connection URLsSteps to Reproduce
Configure the Y-Sweet server with:
Make a request to:
Observe the returned
urlandbaseUrlfields in the responseExpected Behavior
Both URLs should preserve the
/ysweet/path prefix:{ "url": "wss://example.domain.com/ysweet/d/{doc_id}/ws", "baseUrl": "https://example.domain.com/ysweet/d/{doc_id}" }Actual Behavior
The WebSocket URL drops the path prefix, while the base URL is correct:
{ "url": "wss://example.domain.com/d/{doc_id}/ws", "baseUrl": "https://example.domain.com/ysweet/d/{doc_id}" }Root Cause
The issue originates in the
auth_docfunction incrates/y-sweet/src/server.rs(around lines 777–796).WebSocket URL construction (problematic)
url.join("/d/...")treats the argument as an absolute path/ysweet/path entirelyBase URL construction (correct)
Suggested Fix
Construct the WebSocket URL using the same append-style approach as
baseUrlinstead ofurl.join():Impact
/ysweet/)I would be happy to open a PR or help with the fix