Home > Mobile >  Mysterious string behavior in Javascript
Mysterious string behavior in Javascript

Time:12-29

let host = req.headers.host
console.log(host)

Prints localhost:3000. So far so good.

console.log(host === "localhost:3000")

Prints false. Uh?

console.log(host.length)
console.log("localhost:3000".length)

Prints 15 and 14. Our mystery string is one character longer? Let's look at each character one by one.

for (var i = 0; i < host.length; i  ) {
    console.log(i, host.charAt(i));
}

Prints:

0 l
1 o
2 c
3 a
4 l
5 h
6 o
7 s
8 t
9 :
10 6
11 0
12 6
13 4
14 3

Printing one character at a time reveals that there are now 5 digits after the colon: 60643. Whaaat?

This result changes every time I restart my web server. For example, last run it was 63275 and the previous run was 54313.

Adding console.log(host) immediately after the for loop confirms the string is still localhost:3000. What's going on here?

I almost feel crazy asking this question, but I've looked at it all day and this happens 100% of the time. Environment: latest version of Node (v17.3.0), macOS Monterey (M1 mac).


Followups

  • Reproducible example here (requires a free Vercel account)
  • typeof host returns string
  • Here's additional debugging information for each character of the string:
i charAt(i) charCodeAt(i) codePointAt(i).toString(16)
0 l 108 6c
1 o 111 6f
2 c 99 63
3 a 97 61
4 l 108 6c
5 h 104 68
6 o 111 6f
7 s 115 73
8 t 116 74
9 : 58 3a
10 5 53 35
11 4 52 34
12 8 56 38
13 6 54 36
14 2 50 32

CodePudding user response:

According to the documentation:

The vercel dev command is used to replicate the Vercel deployment environment locally, allowing you to test your Serverless Functions, without requiring you to deploy each time a change is made.

For this reason, calls like /api/hello are processed by local proxy server running with a random free port.

And here the most interesting thing begins: for the standard output (stdout) of the proxy server (and of course for the console.log too), the proxy port is replaced with the development port (it feels like it's an outdated legacy):

p.stdout.on('data', (data: string) => {
  process.stdout.write(data.replace(proxyPort, devPort));
});

For this reason, there is a difference in the length of the req.headers.host and localhost:3000, and why they look the same in the console.log, but are not equal when compared.

So it's better for example to use x-forwarded-host header instead of host:

console.log((
  req.headers['x-forwarded-host'] || req.headers.host
) === "localhost:3000");
res.status(200).json(req.headers);
  • Related