-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
core: added capability to pass cookies as CLI parameter #9170
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks very much for picking this up @RynatSibahatau! I left some comments but before we tackle those...
@brendankenny @hoten any thoughts or major changes you would want to propose for this? Linked issue has a tad more discussion but I know you've been playing around with some auth techniques so wanted to get your say on this before going any further.
lighthouse-cli/test/cli/bin-test.js
Outdated
}); | ||
|
||
it('should read extra cookies from file', async () => { | ||
const headersFile = require.resolve('../fixtures/extra-headers/valid.json'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like you mean to use the cookies one instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed by f4e0a49
@@ -105,6 +105,20 @@ function requestHandler(request, response) { | |||
} | |||
} | |||
|
|||
if (params.has('extra_cookie')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this file is for our smoketests, we'll want to make sure we add a smoketest that exercises this functionality which is probably going to be tricky...
maybe hold off on this step until we get buy-in from rest of LH team for this feature to work this way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to keep the changes to this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh @RynatSibahatau think you could add a smoketest exercising this and our cookies logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh @RynatSibahatau think you could add a smoketest exercising this and our cookies logic?
isn't that going to be kind of hard, @patrickhulce? What would we be testing, exactly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking a page has a link or something that contains the contents of the cookies and it gets picked up by the anchor elements artifact we can assert
return; | ||
} | ||
|
||
return this.sendCommand('Network.setCookies', {cookies}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK when the domain/url/path isn't set it will just use the existing target, because we're running this before we've navigated to the page I'm guessing the common case won't actually send the headers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch.
Are we going to entirely defer to the cookie definition for when they're included, e.g. for redirects to different origins, third-party iframes, etc? That format will definitely need to be documented, and it seems like it's going to be a huge pain trying to debug when something goes wrong, but I guess maybe folks are already used to that with other tools doing similar things?
What does WebPageTest do for setting cookies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I remember it is executed on "about:blank" page. This is why it is not really set anything, if not specifying either domain or URL. This another +1 for the bigger JSON structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we going to entirely defer to the cookie definition for when they're included, e.g. for redirects to different origins, third-party iframes, etc? That format will definitely need to be documented, and it seems like it's going to be a huge pain trying to debug when something goes wrong, but I guess maybe folks are already used to that with other tools doing similar things?
What does WebPageTest do for setting cookies?
I would assume we should rely on browser behavior for the session. A cookie is just a can-opener to test page usability under the specific conditions:
- authorize access to content
- allow access to CDN images
- local show locale-specific content
- perform sticky session
- canary routing for CI
I don't think LH is intended to use like E2E test which might need any specific cookie handling between the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RynatSibahatau WDYT about asserting that we have either a url or domain on all the cookies here?
@@ -122,6 +122,21 @@ async function begin() { | |||
cliFlags.extraHeaders = JSON.parse(extraHeadersStr); | |||
} | |||
|
|||
if (cliFlags.extraCookies) { | |||
// TODO: LH.Flags.extraCookies is actually a string at this point, but needs to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
major bummer that we'll have a second one of these before we fix the first, this will also actually run into problems with the config-based CLI flags approach.
maybe we could at least extract this to a function to share between the two?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
major bummer that we'll have a second one of these before we fix the first, this will also actually run into problems with the config-based CLI flags approach.
Yes, definitely, this shouldn't be done a second time :) The TODO even lists how to fix:
either the CLI flag or the setting should have a different name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well that's the partial fix for the path problem, unless I'm misunderstanding?
there's not really an obvious fix I see for the "this thing is a complex object that can't be represented by yargs that we want to JSON.parse from the CLI but just use from node", --cookies-json-string
I guess but that feels 🤢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brendankenny @patrickhulce I am not pretty sure which fix is required here, as I am Java developer. I assume yargs can't provide you correctly typed JSON value out of the box.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right on that point @RynatSibahatau! The best we can do and what the comment suggests is that the property name should just be different for the yargs string version.
@brendankenny thoughts on extraCookiesJsonString
for yargs and extraCookies
for regular? (or we nix the extra
prefix entirely I think was your last suggestion?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump on this @brendankenny
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could do --cookies
for the flag (on the CliFlags
interface) and extraCookies
for the setting (on SharedFlagsSettings
). Or --cookies-string
and cookies
.
Whatever it is, we should absolutely use this pattern. It's awkward, but most folks won't really care what the flag is (shortish is probably better, though), and the TODOs that have propagated from this point can only be removed in the future by doing this, so might as well do it before landing :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote --cookies-string
and --cookies
then! 👍
|
Co-Authored-By: Patrick Hulce <[email protected]>
[RS] It is an array of objects because a cookie is not just a key/value pairs, but also have a big set of parameters (https://github.com/chromedp/cdproto/blob/master/network/network.go#L734). In the underlying protocol, it is also array https://github.com/chromedp/cdproto/blob/master/network/network.go#L825
[RS] LH already has one for headers. Here content might be even bigger. I decided to keep the same behavior. My use case doesn't require this. I am going to pass it via the command line.
[RS] As I mentioned in the first answer it is not a key/value, so if we want to cover all possible use cases it is better to follow with JSON. Also, it avoids extra massaging for passing it. SetHeaders also uses JSON. And JSON is natural for a node language, LH is written on. |
Thanks for educating me @RynatSibahatau! LGTM. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the general approach seems useful, my big questions:
- do we really want to tie ourselves to the protocol's format? It's not necessarily the best or most explicable. On the other hand, it offers a lot of flexibility.
- can we use a different flag name? I dislike "extra headers" ("extra to what?" is a whole explanation that's not needed for 95% of use cases).
--cookies
seems fine to me, if others are ok with that
@@ -122,6 +122,21 @@ async function begin() { | |||
cliFlags.extraHeaders = JSON.parse(extraHeadersStr); | |||
} | |||
|
|||
if (cliFlags.extraCookies) { | |||
// TODO: LH.Flags.extraCookies is actually a string at this point, but needs to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
major bummer that we'll have a second one of these before we fix the first, this will also actually run into problems with the config-based CLI flags approach.
Yes, definitely, this shouldn't be done a second time :) The TODO even lists how to fix:
either the CLI flag or the setting should have a different name.
lighthouse-cli/bin.js
Outdated
let extraCookiesStr = /** @type {string} */ (cliFlags.extraCookies); | ||
// If not a JSON array, assume it's a path to a JSON file. | ||
if (extraCookiesStr.substr(0, 1) !== '[') { | ||
extraCookiesStr = fs.readFileSync(extraCookiesStr, 'utf-8'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, I agree with @hoten that the ambiguity in the overloading of the flag is not worth it (can use the new --cli-flags-path
if want to read from file!), but I do see we have it for --extra-headers
already.
It would be nice to not include it by default and then consider adding it in the future if there's demand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--cli-flags-path
approach looks good for me, I would agree we can avoid this dualism here for cookies. Should I remove it for the headers as well as part of the PR?
If there are no other major concerns for merging the PR. I can start implementing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would love to remove it from headers but that'd be a major breaking change. Added it to our new list for next major version :) #9180
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed the capability to load cookie from file with the same parameter
1677a37
return; | ||
} | ||
|
||
return this.sendCommand('Network.setCookies', {cookies}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch.
Are we going to entirely defer to the cookie definition for when they're included, e.g. for redirects to different origins, third-party iframes, etc? That format will definitely need to be documented, and it seems like it's going to be a huge pain trying to debug when something goes wrong, but I guess maybe folks are already used to that with other tools doing similar things?
What does WebPageTest do for setting cookies?
looks like it's
https://sites.google.com/a/webpagetest.org/docs/using-webpagetest/scripting#TOC-setCookie |
I agree with @RynatSibahatau that the JSON format for setting cookies offers an insane upgrade to dev experience compared to cookie strings. They are the bane of frontend network experience and there's no reason for us to prolong that pain here. I think the default case needs to be a bit streamlined though, i.e. we default the domain to the domain of the target page instead of being a noop on about:blank. |
hmm:
:P |
@brendankenny oh puh-lease it's not like any sufficiently complicated setup requiring cookies would actually involve manually typing the cookies JSON. conversely, for a sufficiently complicated cookies setup automatically crafting the cookies string is a giant headache |
I can totally buy this and I'll defer to you on the point, I just wanted to bring up the existing alternatives (the devil a developer knows can often beat better ergonomics or whatever) and the fact that probably at no point was At the very least the
This comes back to the issue of what to do with redirects, iframes, etc. We'll have to be able to declare which one is the domain. |
All the properties other than URL AFAICT are just the same properties developers should already be familiar with and would try to set with regular cookie syntax anyhoo (and URL just defaults the other properties like secure and domain don't ask why they didn't default https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
Very true. All we have to go on at this point in execution though is the provided URL since we'll need to send that request with the cookie. I think we solve this by just documenting. Maybe we need to warn if there were cookies set but there was a redirect? |
} | ||
|
||
cliFlags.extraCookies = JSON.parse(extraCookiesStr); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
if (!Array.isArray(cliFlags.extraCookies)) { | |
throw new Error('Not a valid JSON array'); | |
} | |
cliFlags.extraCookies.forEach(key => { | |
const cookie = cliFlags.extraCookies[key]; | |
if (! cookie.url || ! cookie.domain) { | |
// Default it to the domain value | |
cookie.url = url; | |
} | |
}); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can suggest this extra check and defaulting for the URL to avoid no-op
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a good idea @RynatSibahatau! I think we would want this in lighthouse-core/
though so node users get this defaulting benefit too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@patrickhulce Could you take a look 074eee1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's exactly what I was hoping for! thanks!
Am I right that you will be able to fix this on your side manually on merge? commit 72c9272
|
@@ -105,6 +105,20 @@ function requestHandler(request, response) { | |||
} | |||
} | |||
|
|||
if (params.has('extra_cookie')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to keep the changes to this file?
@@ -122,6 +122,21 @@ async function begin() { | |||
cliFlags.extraHeaders = JSON.parse(extraHeadersStr); | |||
} | |||
|
|||
if (cliFlags.extraCookies) { | |||
// TODO: LH.Flags.extraCookies is actually a string at this point, but needs to be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could do --cookies
for the flag (on the CliFlags
interface) and extraCookies
for the setting (on SharedFlagsSettings
). Or --cookies-string
and cookies
.
Whatever it is, we should absolutely use this pattern. It's awkward, but most folks won't really care what the flag is (shortish is probably better, though), and the TODOs that have propagated from this point can only be removed in the future by doing this, so might as well do it before landing :)
Hey guys, Do you have any updates on this PR (when it will be merged into master)? I want to let you know that, regarding the functionality, I'm using a fork of @RynatSibahatau's branch for my project and the cookies are correctly set when using the "--extra-cookies" CLI parameter. Thanks, |
The evidence in #6207 (comment) suggests that the Perhaps this suggests not doing any new flag but instead parsing the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your works.
This pipeline has already continued for 2 months, I don't know how long we still need to wait, so I manually copied the code of @RynatSibahatau into my forked repo, and then I push a commit called 'manually merge pr #9170'. If this action is illegal, please tell me, I will delete my forked repo soon.
In my test, I found two bugs, I will comment below:
// the conversion here, but long term either the CLI flag or the setting should have | ||
// a different name. | ||
// @ts-ignore | ||
const extraCookiesStr = /** @type {string} */ (cliFlags.extraCookies); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need handle JSON file, because the readme.md has this usecase.
let extraCookiesStr = /** @type {string} */ (cliFlags.extraCookies);
// If not a JSON object, assume it's a path to a JSON file.
if (extraCookiesStr.substr(0, 1) !== '{') {
extraCookiesStr = fs.readFileSync(extraCookiesStr, 'utf-8');
}
} | ||
extraCookies.forEach(cookie => { | ||
if (!cookie.url || !cookie.domain) { | ||
// Default cookie URL to to current URL, if neither domain nor url is specified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to this comment, should conditions be ?
!cookie.url && !cookie.domain
Can we make a decision on this? This PR, or special casing |
Hey there, Was just wondering if there was an update on when this would be merged into master? Cheers, Chase |
@chasekaylee check out these: https://github.com/GoogleChrome/lighthouse/blob/master/docs/authenticated-pages.md my expectation is those resources are more useful than the feature in this PR. |
ahh very cool. thanks! @paulirish |
Are we moving ahead with this feature? Thanks. Also, @paulirish these links don't work for me. |
I updated the links @koomar. |
@paulirish I think the My 2p is that this MR should be merged. |
Hey Team, Wondering when we can get this merged. Please advise! |
This PR hasn't been updated in almost a full year after there was disagreement on the team about the path forward. Since then, Lighthouse CI and the puppeteer script option offer far greater flexibility and control over these types of situations for the programmatic CI use case and are more consistent with our overall guidance on how to handle authenticated pages than patchwork CLI options with questionable ergonomics. I'm inclined to close this PR rather than resurrect it at this point. |
Thanks for the rapid response, @patrickhulce. Some of us other options like Chrome-Launcher and not puppeteer. Ability to inject extra-cookies would really make Lighthouse a complete solution instead of being tied to be used "only" with puppeteer for such use |
That's fine, you don't have to use puppeteer. If you're already using chrome-launcher you could use something like cri instead to talk to Chrome if you don't like puppeteer's API. The point is that our general solution to this class of problems has been moving in the direction of "setup Chrome however you'd like by talking to it" rather than patching various holes with more and more CLI flags. Now that we have a first-class answer to the CI use case, this feature is unlikely to receive the priority support being requested here. |
I don’t mind closing the PR as I don’t own the Lighthouse product and don’t know details of its roadmap and desired capabilities. I agree that Puppeteer is a more flexible solution. But be alerted that there was some back and forth on Puppeteer repo regarding how correctly set a cookie: puppeteer/puppeteer#1342 I think it is worth providing correct puppeteer script in Lighthouse documentation in regards to how to properly set a cookie. For my use case (it was solved even before I have opened this PR), I was trying to use TEST cookie to reroute canary traffic to the canary version of the service during deployment. I achieved that by switching from cookie-based routing on k8s virtual service to header-based routing. Chrome plugins (like ModHeader) also allows me to specify this header for manual testing of canary version. So, I don’t see any benefit for my specific case to use cookie vs header. Thank you for mentioning this issue on the documentation page! If code owners decide it is good to have CLI parameter, you are welcome to start from the current state of PR. I also can finish it in case there is a consensus within code owners. |
Thanks for the additional context @RynatSibahatau and for all your great work on the PR thusfar! ❤️
This is a great idea 👍 Regardless of the outcome here we should do this.
If that's true, that would unfortunately plague this approach as well. The PR as-is would set the cookie before navigation. |
I'm going to follow up with this solution and we'll close this PR for now. Thank you very much for your work here @RynatSibahatau we do appreciate it and #11313 should make this sort of thing even easier in the future as well. |
Summary
This feature adds the capability to pass cookies as a CLI parameter.
The current proposed approach to use "--extra-headers" parameter for setting cookies has a drawback for a wide range of enterprise applications that operates with the cookies (ex. authentication, localization, etc.).
Any Set-Cookie response from backend overwrites the Cookie header from extra-headers.
It introduces extra complexity of using lighthouse on CI in pure CLI mode (i.e without Puppeteer).
The fix intended to remove this complexity.
lighthouse http://localhost/ --extra-headers "{\"Cookie\":\"test=true\"}"
Request header: Cookie: test=true
Response header: Set-Cookie: locale=en;
All subsequent requests headers:
Actual request headers : Cookie: locale=en
Expected request headers: Cookie: test=true; locale=en
After merging this PR the following command introduced expected behaviour for cookies:
Links:
#6207 (comment)
Related Issues/PRs
fixes #6207