Chrome-remote-interface: Host header is specified and is not an IP address or localhost.

Created on 22 Apr 2018  路  15Comments  路  Source: cyrus-and/chrome-remote-interface

| Component | Version
|-|-
| Operating system | Ubuntu 16.04
| Node.js | 8.11.1
| Chrome/Chromium/... | 66.0.3359.117
| chrome-remote-interface | 0.25.5

Is Chrome running in a container? YES

Getting the error in the latest version of stable chrome

external issue feature request

Most helpful comment

how to override the default host header?

Like this:

--- a/lib/external-request.js
+++ b/lib/external-request.js
@@ -4,6 +4,7 @@ const REQUEST_TIMEOUT = 10000;

 // callback(err, data)
 function externalRequest(transport, options, callback) {
+    options.headers = {host: 'localhost'};
     const request = transport.get(options, function (response) {
         let data = '';
         response.on('data', function (chunk) {

if we can regenerated webSocketDebuggerUrl for temporary workaround, can be work?

It could be but since the webSocketDebuggerUrl issue appears to be a bug of Chrome I would wait a response from the Chromium team.


The simplest workaround is IMHO using the IP address directly.

All 15 comments

From the issue template (emphasis of mine):

Alright then, please provide the following information along with a detailed
description of what went wrong.

Should I guess under what circumstances you get that error?

just connect to the chrome running in a container

$ docker pull yukinying/chrome-headless-browser:66.0.3359.26
$ docker run --cap-add=SYS_ADMIN yukinying/chrome-headless-browser:66.0.3359.26
DevTools listening on ws://0.0.0.0:9222/devtools/browser/12544890-20bd-41b7-a74a-bcc35e4c0cce
$ chrome-remote-interface -t 172.17.0.2 inspect
>>> Browser.getVersion()
...
{ protocolVersion: '1.3',
  product: 'HeadlessChrome/66.0.3359.26',
  revision: '@b3471a2c378d1cfc5be97bb38b9c32038d1aef14',
  userAgent: 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/66.0.3359.26 Safari/537.36',
  jsVersion: '6.6.346.6' }

if you check from none localhost and different subset, you can set the error,

The issue is clear and has nothing to do with containers, the change has been introduced here and now Chrome doesn't allow that, as the error message says, the host header contains a domain other than localhost, the reason is described in this issue which unfortunately is private.

I can see two possible solutions:

  1. I could override the default host header and set it to the empty string;
  2. you can simply specify an IP address instead of a domain as shown in the above comment.

The problem with solution 1. is that the host header is is also used by Chrome to craft the webSocketDebuggerUrl and setting the host to the empty string results in the following:

$ curl -H host: localhost:9222/json/list                                                       
[ {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=/devtools/page/A4566872556EA7F94C344A8ABEA30A73",
   "id": "A4566872556EA7F94C344A8ABEA30A73",
   "title": "about:blank",
   "type": "page",
   "url": "about:blank",
   "webSocketDebuggerUrl": "ws:///devtools/page/A4566872556EA7F94C344A8ABEA30A73"
} ]

And this is something that should be addressed on the Chrome side IMO.

how to override the default host header? if we can regenerated webSocketDebuggerUrl for temporary workaround, can be work?

how to override the default host header?

Like this:

--- a/lib/external-request.js
+++ b/lib/external-request.js
@@ -4,6 +4,7 @@ const REQUEST_TIMEOUT = 10000;

 // callback(err, data)
 function externalRequest(transport, options, callback) {
+    options.headers = {host: 'localhost'};
     const request = transport.get(options, function (response) {
         let data = '';
         response.on('data', function (chunk) {

if we can regenerated webSocketDebuggerUrl for temporary workaround, can be work?

It could be but since the webSocketDebuggerUrl issue appears to be a bug of Chrome I would wait a response from the Chromium team.


The simplest workaround is IMHO using the IP address directly.

So from now on it should work transparently, CRI will resolve the host and use the IP address directly for the HTTP communication.

the workaround doesn't work when the same ip address use for many subdomains

Yes, I guess you need some intermediate agent to do that. My suggestion is to simply get rid of sub domains/virtual hosts to expose Chrome to the network and just rely on the fact that it listens on a different (than any other server) port. If you have many instances change the port number accordingly.

Unfortunately, this is something that I can't solve from here for every possible case.

If you thoroughly describe your scenario I could be more specific.

Hello,
I was stuck at this problem as well until I found a workaround for it.
so my workaround was to override the host header and set it to localhost the replace the actual host by localhost on webSocketDebuggerUrl
my implementation on Golang for this workaround:
https://github.com/chromedp/chromedp/issues/505#issuecomment-549188061

This makes modern versions of chrome-remote-interface fail with Firefox's implementation of Chrome RDP because Firefox requires the Host header to be localhost:12345 (12345 => remote debugger port). Perhaps a simpler/cleaner workaround would be to set Host header explicitly?

options.headers = {Host: `localhost:${options.port}`}

in lib/external-request.js

@lachesis latest chrome-remote-interface seems to work fine with latest Firefox, what scenario are you referring?

We were receiving 400 errors trying to list available debugger endpoints. Perhaps we were running a slightly outdated version. It was definitely a version with the patch from May 2018, but may be missing some later patches that fixed Firefox compatibility.

We decided not to continue with adding Firefox support to our product as their implementation of the Chrome debug protocol is missing some major methods that we need (DOM.describeNode mostly), so I will not be able to investigate further right now.

In my case, I used k8s service name as the link URL, so I bypass the restriction by using DNS lookup to turn host name to IP address, then use it to connect the headless chrome instance.

const dns = require('dns').promises;

const { address } = await dns.lookup("chrome-service", {
  family: 4,
  hints: dns.ADDRCONFIG,
});

const browser = await puppeteer.connect({
  browserURL: `http://${address}:9222`,
});

It works.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fracasula picture fracasula  路  6Comments

sergueyarellano picture sergueyarellano  路  6Comments

paambaati picture paambaati  路  7Comments

puppeteer701 picture puppeteer701  路  4Comments

benjamingr picture benjamingr  路  4Comments