Chainhelen from Thunderbolt

This article describes how the author found a problem with Node.js, which has been fixed in the latest version of Node.js. This article mainly shares the idea and method of locating the problem. When you encounter a difficult problem in development, it is not impossible to rule out the problem of relying on the technology and framework. When you try to find and fix it, I believe that you can not only gain the sense of achievement of contributing code, but also bring the improvement of technical level and confidence.

Problem 1.

A few days ago, when I was testing the Express framework, I constructed a test sample that was dead and dead, even though I was debugging it into the test framework SuperAgent, it still didn’t work. It turned out to be a “problem” with Node.js, and the latest version of Node.js has been “fixed”, causing me to go around a few times without realizing it was node.js.

2. Prepare the environment

  1. Install thegnvm address, later need to control the version (windows10 needs to use the administrator rights of CMD or powershell)
  2. Install the Git environment (mainly usingcurlCommand)
  3. Excerpt the code below
// main.js
var http = require('http')

var tmpObject = Object
tmpObject.prototype['love'] = 'express'
var server = http.createServer(function (_, res) {
  res.setHeader("m"."w")
  res.end()
})
server.listen(3010)
Copy the code

3. Problems recur

  1. Install the Node.js (current stable) version
GNVM install 8.11.2 GNVM use 8.11.2Copy the code
  1. Run the code,Node.js main.js
  2. useThe curl -i 127.0.0.1:3010The command yields the following
$curl -i 127.0.0.1:3010 HTTP/1.1 200 OK M: w e: x Date: Fri, 18 May 2018 14:06:47 GMT Connection: keep-alive Content-Length: 0Copy the code

It makes sense to have a head m: W, but where does e: x come from? Object.prototype.love=’express’

4. Test again

Modify the main.js code and comment out res.setheader (“m”, “w”)

// main.js
var http = require('http')

var tmpObject = Object
tmpObject.prototype['love'] = 'express'
var server = http.createServer(function (_, res) {
  // res.setHeader("m"."w") res.end()}) server.listen(3010) $curl -i 127.0.0.1:3010 HTTP/1.1 200 OK Date: Fri, 18 May 2018 14:30:01 GMT Connection: keep-alive Content-Length: 0Copy the code

It’s gone.

5. Explain

Scroll through v8.11.2 _http_outgoing.js#L497

OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
  ...
  if(! this[outHeadersKey]) this[outHeadersKey] = {}; const key = name.toLowerCase(); this[outHeadersKey][key] = [name, value]; . };Copy the code

So when the code in test 3 executes, it saves the header data like this

this[outHeaderKey] = {
    "m": ["m"."w"]}Copy the code

Also note that the variable initializes this[outHeadersKey] = {}, so this[outHeaderKey]’s prototype chain points to Object.prototype

With that in mind, let’s see what res.end() does, _http_outgoing.end() => _http_server._implICItheader () => _http_server.writehead () => _http_outgoing._storeHeader()

Take a look at _http_server.writehead (), _http_server#L202

    headers = this[outHeadersKey];
    this._storeHeader(statusLine, headers);
Copy the code

Look again at _http_outgoing.storeheader (), _http_server#L307

if (headers === this[outHeadersKey]) {
    for (key inheaders) { var entry = headers[key]; field = entry[0]; value = entry[1]; . }Copy the code

SetHeader (“m”, “w”) :[“m”: “w”] key=m, entry = [m, w], field =m, value = w

Key = ‘love’, entry = ‘express’, field = entry[0] = ‘e’, value = entry[1] = ‘x’, so e:x in the response header comes from this

6. Summary

This is essentially for in traversing the prototype chain, plus node.js’s “weird” way of storing outHeadersKey in arrays

Causes an unintelligible header to appear in the packet sending process

In addition, hasOwnProperty is usually used in projects for in to get around the problem, but this is not the case in the new node.js version. Here is the latest node.js piece of code _http_outgoing.js#L121

 const headers = this[outHeadersKey] = Object.create(null);
Copy the code

Object.create(null) will set the created Object __proto__ to null for in, which will not be iterated. Try GNVM Use V10.1.0

Scan a concern about the thunder front public number