Skip to content

Bug Report: Inconsistent URL construction with url_prefix #424

@gyash1512

Description

@gyash1512

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

  1. Configure the Y-Sweet server with:

    url_prefix = "https://example.domain.com/ysweet/"
  2. Make a request to:

    /doc/{doc_id}/auth
    
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions