Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing header in page.on("response") #4918

Closed
rackaam opened this issue Sep 9, 2019 · 8 comments
Closed

Missing header in page.on("response") #4918

rackaam opened this issue Sep 9, 2019 · 8 comments

Comments

@rackaam
Copy link

rackaam commented Sep 9, 2019

Steps to reproduce

Tell us about your environment:

  • Puppeteer version: 1.14
  • Platform / OS version: Linux Ubuntu
  • URLs (if applicable):
  • Node.js version: 10

What steps will reproduce the problem?

Please include code that reproduces the issue.

  1. Launch server.js
const http = require("http");

const hostname = "127.0.0.1";
const port = 5000;

const server = http.createServer((req, res) => {
  if (req.url === "/one") {
    res.statusCode = 200;
    res.setHeader("Content-Type", "text/html");
    res.setHeader("set-cookie", "_one=1");
    res.end('<html><script src="http://127.0.0.1:5000/two"></script></html>');
  } else if (req.url === "/two") {
    res.statusCode = 200;
    res.setHeader("Content-Type", "application/javascript");
    res.setHeader("set-cookie", "_two=2");
    res.end(`console.log('Hello two');`);
  }
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
  1. Launch this puppeteer script
const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({
    headless: true
  });
  const page = await browser.newPage();

  page.on("response", response => {
    console.log("New response:");
    console.log(response.url());
    console.log(response.headers());
  });

  await page.goto("http://127.0.0.1:5000/one", {
    waitUntil: "load"
  });

  await browser.close();
})();

What is the expected result?
We should get the set-cookie headers for both requests.

What happens instead?

New response:
http://127.0.0.1:5000/one
{ 'content-type': 'text/html',
  'set-cookie': '_one=1',
  date: 'Mon, 09 Sep 2019 09:59:48 GMT',
  connection: 'keep-alive',
  'content-length': '62' }
New response:
http://127.0.0.1:5000/two
{ date: 'Mon, 09 Sep 2019 09:59:48 GMT',
  connection: 'keep-alive',
  'content-length': '25',
  'content-type': 'application/javascript' }

The second set-cookie header is missing.

We can add this lines to the end of the script to confirm the second set-cookie header was successfully interpreted by the browser (but no visible in the response event).

  const client = await page.target().createCDPSession();
  console.log(await client.send("Network.getAllCookies"));
@kdzwinel
Copy link

kdzwinel commented Oct 8, 2019

I've also run into this and can reproduce it in v1.20. It looks like puppeteer is not listening to the Network.responseReceivedExtraInfo event that contains the raw headers (you can do that yourself to work around the issue). DevTools are doing just that:

set-cookie-extra

It also might be a Chromium bug since this cookie is not really blocked, so IMO it should appear in the Network.responseReceived headers.

@rackaam
Copy link
Author

rackaam commented Oct 8, 2019

I think it might be related to https://bugs.chromium.org/p/chromium/issues/detail?id=868407

@robertm-97
Copy link

robertm-97 commented Jul 24, 2020

I had this same issue but I managed to find a workaround. The solution is to listen on the Network.responseReceivedExtraInfo event suggested by #4918 (comment) and extract the cookies from there.
I've created a class that collects the cookies set in request and looks something like this

const setCookie = require('set-cookie-parser');

class HttpCookieInterceptor {
    foundCookies = [];

    httpSetCookiesKey = 'set-cookie';

    constructor(client) {
        // Setup listener for handling finished request
        client.on('Network.responseReceivedExtraInfo', (response) => this.captureHttpCookies(response));
    }

    static async create(page) {
        // open sessions to DevTools
        const client = await page.target().createCDPSession();
        await client.send('Network.enable');
        // Setup request interceptor rules, in this case we will intercept all request
        await client.send('Network.setRequestInterception', {
            patterns: [{
                urlPattern: '*',
            }],
        });
        await client.on('Network.requestIntercepted', async (e) => {
            // Let the request continue, we don't need anything from here
            await client.send('Network.continueInterceptedRequest', {
                interceptionId: e.interceptionId,
            });
        });
        return new HttpCookieInterceptor(client);
    }

    captureHttpCookies(request) {
        if (request && request.headers && request.headers[this.httpSetCookiesKey]) {
            const cookieString = request.headers[this.httpSetCookiesKey];
            const httpCookies = setCookie.parse(cookieString);
            this.foundCookies = [...this.foundCookies, ...httpCookies];
        }
    }

    getCookies() {
        return this.foundCookies;
    }
}

It can be used like this:

const browser = puppeteer.launch({
        headless: true,
        devtools: true,
});
const page = await browser.newPage();
const httpCookieCollector = await HttpCookieInterceptor.create(page);
// do some navigation here
const httpCookies = httpCookieCollector.getCookies();

NOTE: This collects all cookies, you will need to filter duplicates after
Hope this helps

@tobyhinloopen
Copy link

tobyhinloopen commented Aug 2, 2020

@robertm97's reply combined with https://stackoverflow.com/a/49220156 really helped for me. I actually wanted just all headers; I ended up with this code:

async function getApiCallHeaders(page) {
  return new Promise(async (resolve, reject) => {
    resolved = false;
    try {
      const devtools = await page.target().createCDPSession();
      await devtools.send('Network.enable');
      await devtools.send('Network.setRequestInterception', {
        patterns: [{ urlPattern: '*' }],
      });
      devtools.on('Network.requestIntercepted', async (event) => {
        if (resolved) {
          return; // just stop any other request once we get the headers; I only want the headers
        }
        if (/\/me$/.test(event.request.url)) { // < wait for a request matching `/me$`, extract its headers
          resolved = true;
          resolve(event.request.headers);
          return; // just stop any other request once we get the headers; I only want the headers
        }
        await devtools.send('Network.continueInterceptedRequest', {
          interceptionId: event.interceptionId,
        });
      });
      await page.goto("https://...."); // < go to page that loads an app that triggers an API call
    } catch (error) {
      if (!resolved) { // only throw if we didn't resolve earlier; sometimes errors are thrown after being resolved. I don't care about them.
        resolved = true;
        reject(error);
      }
    }
  });
}

I use this code to extract all headers generated by some SPA so I can cheaply continue to call the API using node-fetch without running a browser. IE I login using a browser (because their login flow is convoluted), wait for an API call to happen, extract all headers, kill the browser and continue using the API using node-fetch with the extracted headers.

@And93
Copy link

And93 commented Nov 5, 2020

Maybe someone knows the opposite answer, how to add cookies in response (httpRequest.respond(response)) ? 6578

@verhovsky
Copy link

I switched to using Firefox through playwright, it returns more headers in request.headers:

import playwright from 'playwright';

(async () => {
    const browser = await playwright['firefox'].launch();
    const page = await browser.newPage();

    page.on('request', req => {
        console.log(req.headers());
    });
    await page.goto("https://example.com/");

    await browser.close();
})();

@stale
Copy link

stale bot commented Jun 24, 2022

We're marking this issue as unconfirmed because it has not had recent activity and we weren't able to confirm it yet. It will be closed if no further activity occurs within the next 30 days.

@stale stale bot added the unconfirmed label Jun 24, 2022
@stale
Copy link

stale bot commented Jul 24, 2022

We are closing this issue. If the issue still persists in the latest version of Puppeteer, please reopen the issue and update the description. We will try our best to accomodate it!

@stale stale bot closed this as completed Jul 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants