Repository: puppeteer
Issue: [Bug]: Responses served from cache for a 304 response have unexpected fromCache() and headers
Labels: bug,confirmed,P3

### Bug expectation

When `page.goto` receives a `304: Not Modified` response for which it returns `text()` using a cache, the response reports `fromCache()` as `false` despite returning cached `text()`. It also has some properties which are inconvenient for our use case, but which we aren't sure whether to treat as bugs or expected behavior, including the missing headers from the original 200 response noted originally in #7561:

```
status: 304 // instead of 200 like a client-side cache hit
headers: {
    // missing headers present in original 200 response but not 304 response, such as Content-Type
}
```

We're not sure whether using the status/headers from the 304 response is expected behavior or not; we expected this case to be consistent with the behavior of a client-side cache hit (where, as of the fix for #9965, Puppeteer reports status and headers from the original cached response), but maybe that's not the right expectation.

For our use case, it's specifically inconvenient to not have access to the original 200 response's headers - we'd like to process the `text()` differently based on the `Content-Type` header which, in our case, is present in the original 200 response but not the 304 response.

For reference if other folks encounter this, note that Playwright's behavior in this case is different from Puppeteer's; Playwright 1.33.0 treats this similarly to a client-side caching case and returns the status/headers of the original response.

### Bug behavior

- [ ] Flaky
- [ ] PDF

### Minimal, reproducible example

```TypeScript
const { createHash, randomBytes } = require('node:crypto');
const util = require('node:util');
const http = require('node:http');

const port = 3000;
const throwOnRepro = true; // for puppeteer repro script automation
let browserLauncher = require('puppeteer');
let waitUntil = 'networkidle2';

const etagSeed = randomBytes(16);

const server = http.createServer((req, res) => {
    const hash = createHash('sha256');
    hash.update(etagSeed);
    hash.update(req.url, 'utf-8');
    const etag = `"${hash.digest('hex').slice(0, 12)}"`;

    const ifNoneMatch = req.headers['if-none-match'];
    const suffix = ifNoneMatch ? ` (If-None-Match: ${ifNoneMatch})` : '';
    console.log(`[SERVER] [REQUEST ] ${req.url}${suffix}`);
    
    if (req.url === '/redirect') {
        res.setHeader('Location', `http://localhost:${port}/#from-redirect`)
        res.writeHead(302);
        res.end();
        console.log('[SERVER] [RESPONSE] 302 to /#from-redirect')
    } else if (req.url !== '/') {
        res.writeHead(404);
        res.end();
        console.log('[SERVER] [RESPONSE] 404')
    } else if (ifNoneMatch === etag) {
        res.writeHead(304);
        res.end();
        console.log('[SERVER] [RESPONSE] 304')
    } else {
        res.setHeader("Content-Type", "text/html; charset=utf-8");
        res.setHeader('Cache-Control', `max-age=0`);
        res.setHeader('ETag', etag);
        res.writeHead(200);
        res.end(`
            <!DOCTYPE html>
            <h1>Inside index.html</h1>
            <div id="client-root">Content!</div>
            <script>
            window.addEventListener('load', () => {
                if(window.location.hash !== '#from-redirect') {
                    console.log('Initiating redirect via window.location.assign...')
                    window.location.assign('redirect')
                } else {
                    document.getElementById('client-root').innerText = 'Detected as from redirect'
                    history.replaceState("", document.title, window.location.pathname);
                }
            });
            </script>
        `);
        console.log(`[SERVER] [RESPONSE] 200 (ETag: ${etag})`);
    }
});

async function logResponse(response) {
    const request = response.request();
    responseDebugInfo = {
        url: response.url(),
        status: response.status(),
        statusText: response.statusText(),
        headers: response.headers(),
        redirectChainUrls: request.redirectChain?.()?.map(req => req.url()) ?? undefined,
        fromCache: response.fromCache?.(),
        text: await response.text()
    };

    console.log(util.inspect(responseDebugInfo));
}

server.listen(port, 'localhost', async () => {
    console.log(`Server listening on port ${port}, starting test`);

    const browser = await browserLauncher.launch({ headless: false });
    const page = await browser.newPage();

    console.log(`first await page.goto('http://localhost:${port}/')`)
    const response = await page.goto(`http://localhost:${port}/`, { waitUntil });
    await logResponse(response);
    
    await browser.close();

    if (throwOnRepro) {
        if(!response.fromCache()) {
            throw new Error(`Response text() came from cache (status ${response.status()}), but fromCache() is ${response.fromCache()}`);
        } else if(response.headers['Content-Type'] !== 'text/html; charset=utf-8') {
            throw new Error(`Response has unexpected Content-Type: ${response.headers['Content-Type']}`);
        }
    } else {
        console.log(`Leaving test server running at http://localhost:${port} for manual testing, Ctrl+C to exit`);
    }
});
```


### Error string

Response text() came from cache (status 304), but fromCache() is false

### Puppeteer configuration

_No response_

### Puppeteer version

20.1.2

### Node version

18.16.0

### Package manager

npm

### Package manager version

9.5.1

### Operating system

Windows