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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions