Skip to content

**Issue Title:** xtask serve blocks on connection requests causing slow page loads (Includes Fix) #42

@Adithyakp86

Description

@Adithyakp86

Describe the bug

The local development server implemented in cargo xtask serve (xtask/src/main.rs:cmd_serve()) is currently fully synchronous and single-threaded. It processes incoming HTTP connections sequentially with a blocking .read() call.

When modern browsers attempt to load a locally served mdBook page, they open multiple concurrent connections to fetch HTML, CSS, JavaScript, and font assets simultaneously. However, because the server handles only one connection at a time, these requests enqueue. Furthermore, the server does not send a Connection: close header but silently drops the socket after writing the response.

This causes browsers to hang onto Keep-Alive connections indefinitely, leading to artificially slow page rendering, missing CSS/JS if the connections time out, or "Connection Reset" errors. It behaves like a local Denial of Service (DoS) resulting from synchronous I/O.

Steps To Reproduce

  1. Run the local preview server with: cargo xtask serve
  2. Open a web browser and navigate to http://127.0.0.1:3000.
  3. Open the browser's developer tools (Network tab) and disable caching.
  4. Hard-refresh the page a few times.
  5. Notice that some assets (CSS/JS files) take long to start downloading or fail to load completely.

The Solution / Code Fix

To fix this natively without adding new heavy dependencies, we simply need to:

  1. Spawn a thread for each incoming connection (std::thread::spawn).
  2. Add Connection: close to the HTTP response headers to inform the browser that the socket won't be reused for Keep-Alive requests.

Here is the exact code patch for xtask/src/main.rs inside the cmd_serve function:

    for stream in listener.incoming() {
        let Ok(mut stream) = stream else { continue };
        
        // Clone the pathbuf so we can move it into the thread
        let site_canon = site_canon.clone();

        std::thread::spawn(move || {
            let mut buf = [0u8; 4096];
            let n = stream.read(&mut buf).unwrap_or(0);
            let request = String::from_utf8_lossy(&buf[..n]);

            let path = request
                .lines()
                .next()
                .and_then(|line| line.split_whitespace().nth(1))
                .unwrap_or("/");

            if let Some(file_path) = resolve_site_file(&site_canon, path) {
                let body = std::fs::read(&file_path).unwrap_or_default();
                let mime = guess_mime(&file_path);
                
                // ADDED: Connection: close
                let header = format!(
                    "HTTP/1.1 200 OK\r\nContent-Type: {mime}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
                    body.len()
                );
                
                let _ = stream.write_all(header.as_bytes());
                let _ = stream.write_all(&body);
            } else {
                let body = b"404 Not Found";
                
                // ADDED: Connection: close
                let header = format!(
                    "HTTP/1.1 404 Not Found\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
                    body.len()
                );
                
                let _ = stream.write_all(header.as_bytes());
                let _ = stream.write_all(body);
            }
        });
    }



**Assign this isuue i will solve.**

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