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

Verification of baseUrl is failing when using hosts in cypress.json #20647

Closed
dani8art opened this issue Mar 16, 2022 · 12 comments
Closed

Verification of baseUrl is failing when using hosts in cypress.json #20647

dani8art opened this issue Mar 16, 2022 · 12 comments
Assignees

Comments

@dani8art
Copy link

dani8art commented Mar 16, 2022

Current behavior

Having a cypress.json like following

{
  "baseUrl": "http://wordpress.local",
  "hosts": {
    "wordpress.local": "121.0.0.1"
  }
}

When running cypress we get an unexpected behavior, Cypress fails to verify the URL.

DEBUG=cypress:network:connect,cypress:server:server-e2e,cypress:server:server-base cypress run -P /tmp/wordpress/cypress
[30:0316/162352.034421:ERROR:bus.cc(392)] Failed to connect to the bus: Failed to connect to socket /run/dbus/system_bus_socket: No such file or directory
[30:0316/162352.036782:ERROR:bus.cc(392)] Failed to connect to the bus: Address does not contain a colon
[30:0316/162352.036984:ERROR:bus.cc(392)] Failed to connect to the bus: Address does not contain a colon
[202:0316/162352.061512:ERROR:gpu_init.cc(453)] Passthrough is not supported, GL is swiftshader, ANGLE is
  cypress:server:server-base server open +0ms
  cypress:server:server-e2e createServer connecting to server +0ms
  cypress:server:server-base Server listening on  { address: '127.0.0.1', family: 'IPv4', port: 36635 } +22ms
  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +0ms
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '192.168.1.107' } +2ms
Cypress could not verify that this server is running:

  > http://wordpress.local/

We are verifying this server because it has been configured as your baseUrl.

Cypress automatically waits until your server is accessible before running tests.

We will try connecting to it 3 more times...
  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +3s
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '192.168.1.107' } +1ms
We will try connecting to it 2 more times...
  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +3s
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '192.168.1.107' } +1ms
We will try connecting to it 1 more time...

  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +4s
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '192.168.1.107' } +2ms
  cypress:server:server-e2e
  cypress:server:server-e2e AggregateError of:
  cypress:server:server-e2e     Error: connect ECONNREFUSED 127.0.0.1:80
  cypress:server:server-e2e
  cypress:server:server-e2e     at SomePromiseArray._checkOutcome (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/some.js:82:17)
  cypress:server:server-e2e     at SomePromiseArray._promiseRejected (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/some.js:69:17)
  cypress:server:server-e2e     at Promise._settlePromise (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:576:26)
  cypress:server:server-e2e     at Promise._settlePromise0 (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:614:10)
  cypress:server:server-e2e     at Promise._settlePromises (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:690:18)
  cypress:server:server-e2e     at _drainQueueStep (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/async.js:138:12)
  cypress:server:server-e2e     at _drainQueue (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/async.js:131:9)
  cypress:server:server-e2e     at Async._drainQueues (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/async.js:147:5)
  cypress:server:server-e2e     at Immediate.Async.drainQueues [as _onImmediate] (/root/.cache/Cypress/9.5.2/Cypress/resources/app/node_modules/bluebird/js/release/async.js:17:14)
  cypress:server:server-e2e     at processImmediate (node:internal/timers:464:21)
  cypress:server:server-e2e  +10s
  cypress:server:server-base Setting remoteAuth undefined +10s
  cypress:server:server-base Setting remoteOrigin http://wordpress.local +2ms
  cypress:server:server-base Setting remoteHostAndPort { port: '80', tld: 'local', domain: 'wordpress' } +0ms
  cypress:server:server-base Setting remoteDocDomain wordpress.local +0ms
  cypress:server:server-base Getting remote state: { auth: undefined, props: { port: '80', tld: 'local', domain: 'wordpress' }, origin: 'http://wordpress.local', strategy: 'http', visiting: undefined, domainName: 'wordpress.local', fileServer: null } +1ms
Cypress failed to verify that your server is running.

However if we remove baseUrl from the cypress.json and we use cy.visit('http://wordpress.local/'); instead of cy.visit('/'); it work perfect.

{
  "hosts": {
    "wordpress.local": "121.0.0.1"
  }
}
  cypress:server:server-base server open +0ms
  cypress:server:server-e2e createServer connecting to server +0ms
  cypress:server:server-base Server listening on  { address: '127.0.0.1', family: 'IPv4', port: 32997 } +19ms
  cypress:server:server-base Setting remoteAuth undefined +36ms
  cypress:server:server-base Setting remoteOrigin http://localhost:32997 +1ms
  cypress:server:server-base Setting remoteStrategy file +0ms
  cypress:server:server-base Setting remoteHostAndPort null +0ms
  cypress:server:server-base Setting remoteDocDomain localhost +0ms
  cypress:server:server-base Setting remoteFileServer http://localhost:32783 +0ms
  cypress:server:server-base Getting remote state: { auth: undefined, props: null, origin: 'http://localhost:32997', strategy: 'file', visiting: undefined, domainName: 'localhost', fileServer: 'http://localhost:32783' } +1ms
====================================================================================================

  (Run Starting)

  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Cypress:        9.4.1                                                                          │
  │ Browser:        Electron 94 (headless)                                                         │
  │ Node Version:   v14.19.0 (/usr/bin/node)                                                       │
  │ Specs:          1 found (wordpress_spec.js)                                                    │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘
...
  cypress:server:server-base Getting remote state: { auth: null, props: { port: '80', tld: 'local', domain: 'wordpress' }, origin: 'http://wordpress.local', strategy: 'http', visiting: false, domainName: 'wordpress.local', fileServer: null } +1ms
  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +464ms
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '192.168.1.107' } +1ms

Desired behavior

We are using a Linux base container like cypress/included:9.4.1 in our CI system so the error happens. However, it doesn't happen locally using MacOS. You can see the following logs

$ DEBUG=cypress:network:connect,cypress:server:server-e2e,cypress:server:server-base cypress run -P /examples/wordpress/cypress
  cypress:server:server-base server open +0ms
  cypress:server:server-e2e createServer connecting to server +0ms
  cypress:server:server-base Server listening on  { address: '127.0.0.1', family: 'IPv4', port: 49206 } +11ms
  cypress:network:connect beginning getAddress { hostname: 'wordpress.local', port: 80 } +0ms
  cypress:network:connect got addresses { hostname: 'wordpress.local', port: 80, addresses: '127.0.0.1' } +1ms
  cypress:server:server-base Setting remoteAuth undefined +23ms
  cypress:server:server-base Setting remoteOrigin http://wordpress.local +0ms
  cypress:server:server-base Setting remoteHostAndPort { port: '80', tld: 'local', domain: 'wordpress' } +0ms
  cypress:server:server-base Setting remoteDocDomain wordpress.local +1ms
  cypress:server:server-base Getting remote state: { auth: undefined, props: { port: '80', tld: 'local', domain: 'wordpress' }, origin: 'http://wordpress.local', strategy: 'http', visiting: undefined, domainName: 'wordpress.local', fileServer: null } +0ms
...
  (Run Starting)

  ┌────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Cypress:        9.4.1                                                                          │
  │ Browser:        Electron 94 (headless)                                                         │
  │ Node Version:   v14.19.0 (/Users/darteaga/.nvm/versions/node/v14.19.0/bin/node)                │
  │ Specs:          1 found (wordpress_spec.js)                                                    │
  └────────────────────────────────────────────────────────────────────────────────────────────────┘


───────────────────

The desired behavior is this working on a Linux system. The problem seems to be related to the verification of the baseUrl that is performed when it is found in the cypress.jso.

Test code to reproduce

Cypress.json

{
  "baseUrl": "https://google.local"
  "hosts": {
    "google.local": "216.58.214.142"
  }
}

spec.js

/// <reference types="cypress" />

it('Google is shown', () => {
    cy.visit('/');
    cy.get('img.lnXdpd').should('have.attr', 'src').should('include','googlelogo_color_272x92dp.png');
});

Cypress Version

9.4.1

Other

$ cypress --version
Cypress package version: 9.4.1
Cypress binary version: 9.4.1
Electron version: 15.3.4
Bundled Node version: 16.5.0
@dani8art
Copy link
Author

Hi 👋🏼 @jennifer-shehane, @chrisbreiding, can anyone take a look at this issue? This hosts feature is critical for us when testing complex authentication workflows like OAuth.

We have developed a workaround to bypass the initial checks but it would be great to get this issue fixed.

workaround

cypress.json

{
    "hosts": {
      "my.ip": "127.0.0.1"
    },
    "env": {
      "baseUrl": "https://my.ip"
    }
}

support/index.js

// TODO: remove this WORKAROUND for https://github.com/cypress-io/cypress/issues/20647,
//   then you should use baseUrl instead of env.baseUrl
Cypress.Commands.overwrite('visit', (original, ...args) => {
  if (!Cypress.env("baseUrl")) {
    throw new Error("Not found $.env.baseUrl but it is required");
  }

  const relative = args.shift();
  const target = new URL(relative, new URL(Cypress.env("baseUrl")));

  return new Promise((resolve) => {
    resolve(original(target.toString(), ...args));
  });
});

Thank you so much in advance.

@ManuelLR
Copy link

Hi!
We have encountered the same problem but in our case, we cannot use the workaround proposed by @dani8art.

We need hosts and baseUrl configs working at the same time because we need to map the domain to an IP and, additionally, we need to avoid the "reloading error" in the middle of a test whose solution is to define the baseUrl ( tickets: #2636, #3454, #2777 ... ).

So this is also critical for us and we can not use the workaround.

@funktionalsystems
Copy link

This is apparently a side-effect of how #383 was originally implemented: https://github.com/cypress-io/cypress/blame/547b700331c251410a1d17872d2a19c2009dec42/packages/server/lib/server-e2e.ts#L103-L125

Perhaps instead of just if (baseUrl) this should be something like if (baseUrl && configWaitForBaseUrl) (hypothetical config var)

If/when #22676 is implemented, this should be kept in mind as well.

@dani8art
Copy link
Author

If/when #22676 is implemented, this should be kept in mind as well.

@funktionalsystems you are right, we should keep it

However, this case is a bit special because we want the check to be performed. The issue here is that the $.hosts property is not taken into account to do this check.

Additionally, it only happens for Linux environments, so it shouldn't be because of the piece of code you attached.

@funktionalsystems
Copy link

@dani8art I agree that fixing hosts resolution is a much better solution that disabling the check.
For now, I've reformulated your workaround above for the new v10+ config system.
It works for me, and I wonder if it would also solve the issues @ManuelLR was having?

cypress/support/e2e.js:

beforeEach(() => {
  // There is currently a bug preventing `hosts` config from working when `baseUrl` is set:
  // https://github.com/cypress-io/cypress/issues/20647
  // Work around this by specifying `baseUrl` via an innocent env var, and then (if it has a value) inject that.
  // Do this in `beforeEach` because https://docs.cypress.io/api/cypress-api/config says
  // "Configuration set using Cypress.config is only in scope for the current spec file."
  let x = Cypress.env("CYPRESSTEST_URL");
  if(x) {
    Cypress.config("baseUrl", x);
  }
})

@funktionalsystems
Copy link

Additionally, it only happens for Linux environments, so it shouldn't be because of the piece of code you attached.

@dani8art I am also on Linux (only) - I don't have a good way to test other platforms, but very odd if it doesn't happen elsewhere.

It seems like it HAS to be that chunk of code - That's the only place that emits CANNOT_CONNECT_BASE_URL ("Cypress could not verify that this server is running")

DNS overrides appear to be correctly checked by getAddress in packages/network/lib/connect.ts (last touched by !3531)
Call chain:

	packages/server/lib/util/ensure-url.ts
	retryIsListening(
		isListening(
			connect.getAddress(Number(port), String(hostname)) 
				packages/network/lib/connect.ts
					// promisify at the very last second which enables us to
					// modify dns lookup function (via hosts overrides)
					const lookupAsync = Bluebird.promisify<LookupAddress[], string, LookupAllOptions>(dns.lookup, { context: dns })

And DNS overrides appear to be correctly set by createHosts in packages/server/lib/server-base.ts
Call chain: server-base.open -> createHosts -> evilDns.add

And in general the DNS overrides appear to work correctly later on in the tests, as long as we don't get stopped by the baseUrl bug.

So there must be some kind of runtime scope/timing issue where it's not getting set before being used, even though it looks "ok" lexically. I'm no JS/TS expert, but is this some async/await/promises/Bluebird shenanigans?
Call chain from packages/server/lib/project-base.ts:

	async open () {
		debug('opening project instance %s'
		this.createServer -> ServerE2E#constructor() -> ServerBase#constructor()
		await this._server.open(
			server-e2e.open -> server-base.open 
			server-base.open
				debug('server open')
				-> createHosts -> evilDns.add  (TODO: a debug log here might help verify timing!)
				-> (abstract)this.createServer
				server-e2e.createServer
					new Bluebird
						debug('createServer connecting to server')
						this.server -> server-base.ensureProp cypress/packages/server/lib/util/class-helpers.ts
							(If this were a problem, it would throw Error `ServerE2E#open must first be called before accessing 'this.server'`)
						this._listen().then()
							Bluebird.all([]).spread()
								ensureUrl.isListening(baseUrl)
		debug('project config: 

Again, I'm not expert, but that last part (last touched by !14479) looks especially suspect to me.

@funktionalsystems
Copy link

funktionalsystems commented Nov 29, 2022

Actually, it looks like dns is resolving hosts ok in all cases. - Perhaps this is somehow a bug in isListening (ensure-url.ts)

Here's (sanitized) what I get when running with --config baseUrl=https://my.redacted.example.com,hosts={\"*.redacted.example.com\":\"$MY_REDACTED_IP\"}

  cypress:server:server-base server open +0ms
  cypress:server:server-e2e createServer connecting to server +0ms
  cypress:server:server-base Server listening on  { address: '127.0.0.1', family: 'IPv4', port: 46389 } +31ms
  cypress:network:connect beginning getAddress { hostname: 'my.redacted.example.com', port: 443 } +0ms
  cypress:network:connect got addresses { hostname: 'my.redacted.example.com', port: 443, addresses: '[MASKED]' } +1ms
Cypress could not verify that this server is running:
  > https://my.redacted.example.com
We are verifying this server because it has been configured as your baseUrl.
Cypress automatically waits until your server is accessible before running tests.
We will try connecting to it 3 more times...
  cypress:network:connect beginning getAddress { hostname: 'my.redacted.example.com', port: 443 } +3s
  cypress:network:connect got addresses { hostname: 'my.redacted.example.com', port: 443, addresses: '[MASKED]' } +0ms
We will try connecting to it 2 more times...
  cypress:network:connect beginning getAddress { hostname: 'my.redacted.example.com', port: 443 } +3s
  cypress:network:connect got addresses { hostname: 'my.redacted.example.com', port: 443, addresses: '[MASKED]' } +1ms
We will try connecting to it 1 more time...
  cypress:network:connect beginning getAddress { hostname: 'my.redacted.example.com', port: 443 } +4s
  cypress:network:connect got addresses { hostname: 'my.redacted.example.com', port: 443, addresses: '[MASKED]' } +1ms
  cypress:server:server-e2e AggregateError: aggregate error
  cypress:server:server-e2e     at SomePromiseArray._checkOutcome (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/some.js:82:17)
  cypress:server:server-e2e     at SomePromiseArray._promiseRejected (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/some.js:69:17)
  cypress:server:server-e2e     at Promise._settlePromise (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:576:26)
  cypress:server:server-e2e     at Promise._settlePromise0 (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:614:10)
  cypress:server:server-e2e     at Promise._settlePromises (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/promise.js:690:18)
  cypress:server:server-e2e     at _drainQueueStep (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/async.js:138:12)
  cypress:server:server-e2e     at _drainQueue (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/async.js:131:9)
  cypress:server:server-e2e     at Async._drainQueues (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/async.js:147:5)
  cypress:server:server-e2e     at Async.drainQueues [as _onImmediate] (/.cache/Cypress/11.1.0/Cypress/resources/app/node_modules/bluebird/js/release/async.js:17:14)
  cypress:server:server-e2e     at process.processImmediate (node:internal/timers:466:21) +10s
Cypress failed to verify that your server is running.
Please start this server and then run Cypress again.

The [MASKED] is my correct IP. So somehow it is resolving yet still "failing." If I use my previously mentioned workaround and run with --env "CYPRESSTEST_URL=https://my.redacted.example.com" --config hosts={\"*.redacted.example.com\":\"$MY_REDACTED_IP\"} it skips the check and works fine.

@dani8art
Copy link
Author

dani8art commented Dec 1, 2022

Wow, @funktionalsystems this is really great research, Since I saw that it was working depending on the OS I thought it might be related to "be some kind of runtime scope/timing issue".

However, I can see this line

cypress:server:server-base Server listening on  { address: '127.0.0.1', family: 'IPv4', port: 46389 }

Which is always pointing to 127.0.0.1 instead of being pointing to $MY_REDACTED_IP as you have mentioned it may be related to "Perhaps this is somehow a bug in isListening (ensure-url.ts)"

@cypress-app-bot
Copy link
Collaborator

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

@cypress-app-bot cypress-app-bot added the stale no activity on this issue for a long period label May 31, 2023
@henryruhs
Copy link

Do not close, issue still exists.

@nagash77 nagash77 added Needs Reproduction and removed stage: backlog stale no activity on this issue for a long period labels Jun 12, 2023
@mryellow
Copy link

Got to love when bots mark issues as stale. "We haven't fixed this, I know, hide it!"

@mschile
Copy link
Contributor

mschile commented Jun 13, 2023

Hi everyone, unfortunately, the hosts config option is not officially supported and is not listed in our configuration documentation. As such, I am going to close this issue. However, please feel free to continue any discussions. If someone would like to make an official feature request for the hosts option, please create a feature request here.

@mschile mschile closed this as not planned Won't fix, can't repro, duplicate, stale Jun 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants