关于javascript:http-server源码解析

38次阅读

共计 9108 个字符,预计需要花费 23 分钟才能阅读完成。

本文次要过下 http 生成服务和解决申请的次要流程,其余性能并未波及。

应用例子

const http = require('http');

http.createServer((req, res) => {res.end('hello word');
}).listen(8080);

例子中从生成服务,到接管申请,最初响应申请,其中次要的工作有 4 局部,别离是:

  • 调用 http.createServer 来生成一个服务
  • 调用 listen 函数监听端口
  • 接管申请,生成 reqres对象
  • 执行业务函数,执行 res.end 响应申请

http.createServer 和 listen

// lib/http.js
function createServer(opts, requestListener) {return new Server(opts, requestListener);
}

// lib/_http_server.js
function Server(options, requestListener) {if (typeof options === 'function') {
    requestListener = options;
    options = {};}
  // ...
  if (requestListener) {
    // 当 req 和 res 对象都生成好当前,就会触发 request 事件,让业务函数对申请进行解决
    this.on('request', requestListener);
  }

  // connection 事件能够在 net Server 类中看到,当三次握手实现后,就会触发这个事件
  this.on('connection', connectionListener);
}
ObjectSetPrototypeOf(Server.prototype, net.Server.prototype);
ObjectSetPrototypeOf(Server, net.Server);

function connectionListener(socket) {
  // 这里就是执行 connectionListenerInternal 函数并传入 this 和 socket 参数
  defaultTriggerAsyncIdScope(getOrSetAsyncId(socket), connectionListenerInternal, this, socket
  );
}

// connection 事件触发后的回调函数,这个函数将在“解析生成 req、res 对象”板块进行解说
function connectionListenerInternal(server, socket) {// ...}

调用 http.createServer 函数时,会返回一个 Server 实例,Server是从 net Server 类继承而来的。因而,http Server实例也就具备监听端口生成服务,与客户端通信的能力。后面例子中调用的 listen 函数,实际上就是 net Server 中的listen

在实例 Server 对象的过程中,会别离监听 requestconnection这两个事件。

  • connection:这里监听的就是 net 中的 connection 事件,当客户端发动申请,TCP 三次握手连贯胜利时,服务端就会触发 connection 事件。connection事件的回调函数 connectionListenerInternal 将在下一个板块进行解说。
  • request:当 reqres对象都初始胜利当前,就会公布 request 事件,后面代码中咱们能够看到 request 事件的回调函数 requestListener 就是开发者调用 http.createServer 时传入的回调函数,这个回调函数会接管 reqres两个对象。

生成 req、res 对象

当客户端 TCP 申请与服务端连贯胜利后,服务端就会触发 connection 事件,此时就会实例一个 http-parser 用来解析客户端申请,当客户端数据解析胜利后,就会生成一个 req 对象,接下来咱们先来看下 req 对象生成过程。

// lib/_http_server.js
function Server(options, requestListener) {
  // ...
  // 客户端与服务端三次握手实现,触发 connection 事件
  this.on('connection', connectionListener);
}

function connectionListener(socket) {
  // 这里就是执行 connectionListenerInternal 函数并传入 this 和 socket 参数
  defaultTriggerAsyncIdScope(getOrSetAsyncId(socket), connectionListenerInternal, this, socket
  );
}

/**
 * @param {http Server} server
 * @param {net Socket} socket
 */
function connectionListenerInternal(server, socket) {
  // ...
  // parsers.alloc 函数执行会应用返回一个 free list 调配的 HTTPParser 对象
  const parser = parsers.alloc();
  // 申请解析器初始化工作
  parser.initialize(
    HTTPParser.REQUEST,
    new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
    server.maxHeaderSize || 0,
    server.insecureHTTPParser === undefined ?
      isLenient() : server.insecureHTTPParser,
    server.headersTimeout || 0,
  );
  parser.socket = socket;
  socket.parser = parser;
  // ...
}

// lib/_http_common.js
const parsers = new FreeList('parsers', 1000, function parsersCb() {
  // 这里应用 http-parser 库来作为申请解析器
  const parser = new HTTPParser();
  cleanParser(parser);
  // ...
  return parser;
});

http Server中应用 http-parser 实例来作为客户端申请的解析器。值得注意的是,这里应用了 free list 数据结构来调配 parser 对象。

// lib/internal/freelist.js
class FreeList {constructor(name, max, ctor) {
    this.name = name;
    this.ctor = ctor;
    this.max = max;
    this.list = [];}

  // 须要对象,调配一个对象
  alloc() {
    return this.list.length > 0 ?
      this.list.pop() :
      // 这里的 ctor 是实例 FreeList 对象时,传入的对立新增对象的办法
      ReflectApply(this.ctor, this, arguments);
  }

  // 对象用完,开释对象
  free(obj) {if (this.list.length < this.max) {this.list.push(obj);
      return true;
    }
    return false;
  }
}

这部分使用到 free list 数据结构。应用该数据结构目标是缩小对象新建销毁所带来的性能耗费,它会保护一个长度固定的队列,队列中的所有对象大小都雷同。当须要应用对象的时候,会优先从队列中获取闲暇的对象,如果队列中曾经没有可用的对象,就会新建一个与队列中寄存的对象大小雷同的对象,供程序应用。对象应用完后,不会间接销毁,而是会将对象压入队列中,直到前面被推出应用。

理解 free list 后,咱们持续来看下客户端申请的解析。

// lib/_http_common.js
const parsers = new FreeList('parsers', 1000, function parsersCb() {const parser = new HTTPParser();

  cleanParser(parser);

  // 为这些事件绑定回调函数
  parser[kOnHeaders] = parserOnHeaders;
  parser[kOnHeadersComplete] = parserOnHeadersComplete;
  parser[kOnBody] = parserOnBody;
  parser[kOnMessageComplete] = parserOnMessageComplete;

  return parser;
});

http-parser 在解析客户端申请也是基于事件来对数据进行解决:

  • kOnHeaders:一直解析申请头
  • kOnHeadersComplete:申请头解析实现
  • kOnBody:一直解析申请体
  • kOnMessageComplete:申请体解析实现

TCP 在进行数据传输的过程中,会将超出缓冲区残余空间大小的数据进行拆包,使得同一个申请数据包可能分屡次发送给服务端。这里 kOnHeaderskOnBody就是用于拼接被拆分的数据,组合同一个申请的数据。

当申请头解析实现当前,会执行 kOnHeadersComplete 回调函数,在这个回调函数中会生成 req 对象。

// lib/_http_common.js
const {IncomingMessage} = require('_http_incoming');
// 申请头解析实现后执行的回调函数
function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, url, statusCode, statusMessage, upgrade, shouldKeepAlive) {
  const parser = this;
  const {socket} = parser;
  // ...
  // 绝大多数状况下 socket.server[kIncomingMessage]等于 IncomingMessage
  const ParserIncomingMessage = (socket && socket.server && socket.server[kIncomingMessage]) || IncomingMessage;
  const incoming = parser.incoming = new ParserIncomingMessage(socket);
  // ...
  return parser.onIncoming(incoming, shouldKeepAlive);
}

// lib/_http_incoming.js
function IncomingMessage(socket) {// ...}

kOnHeadersComplete回调中实例进去的 IncomingMessage 对象就是 req 对象。回调最初会执行 parser.onIncoming 函数,生成 res 对象。

// lib/_http_server.js
function connectionListenerInternal(server, socket) {
  // ...
  // 这个就是 kOnHeadersComplete 回调最初执行的函数
  parser.onIncoming = FunctionPrototypeBind(parserOnIncoming, undefined, server, socket, state);
  // ...
}

// 第四个参数就是 req 对象,req 对象是在 parser.onIncoming(incoming, shouldKeepAlive)函数执行的时候传入的 incoming 对象
function parserOnIncoming(server, socket, state, req, keepAlive) {
  // ...
  ArrayPrototypePush(state.incoming, req);

  // 实例 res 对象
  const res = new server[kServerResponse](req);

  if (socket._httpMessage) {ArrayPrototypePush(state.outgoing, res);
  }

  // ...
  // 这个事件会在调用 res.end 的时候触发
  res.on('finish', FunctionPrototypeBind(resOnFinish, undefined, req, res, socket, state, server));
  // ...
    server.emit('request', req, res); // 公布 request 事件,执行 createServer 函数调用传入的业务处理函数
  // ...
}

// 这里的 ServerResponse 继承于 OutgoingMessage 类,后续将会介绍到
this[kServerResponse] = options.ServerResponse || ServerResponse;

reqres对象都初始胜利并寄存后,就会执行 createServer 函数调用传入的业务处理函数。
req 生成后,边会执行 parserOnIncoming 生成 res 对象,同时会在 res 对象中注册 finish 事件,当业务代码执行 res.end 的时候,就会触发这个事件。当 reqres对象都筹备好后,就会公布 request 事件,同时将 reqres对象传入。request事件的回调函数就是业务代码调用 http.createServer 时传入的回调函数。

res.end 执行

const http = require('http');

http.createServer((req, res) => {res.end('hello word');
}).listen(8080);

当业务解决实现后,业务代码中被动调用 res.end() 函数,响应客户端申请,接下来咱们看下。

// lib/_http_server.js
function ServerResponse(req) {FunctionPrototypeCall(OutgoingMessage, this);
  // ...
}

ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype);
ObjectSetPrototypeOf(ServerResponse, OutgoingMessage);

ServerResponse类是从 OutgoingMessage 类继承的。业务中应用的 res.end 办法也是在 OutgoingMessage 中进行定义的,上面咱们看下 OutgoingMessage 类实现。

// lib/_http_outgoing.js
function OutgoingMessage() {
  // ...
  this._header = null;
  // ...
}

OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {
  //...
  if (chunk) {
    // ...
    write_(this, chunk, encoding, null, true);
  }

  // 订阅 finish 事件,回调函数是 res.end 调用时传入的 callback
  if (typeof callback === 'function')
    this.once('finish', callback);

  // ...
    // 应用 write_将响应数据写入响应申请的内容中,而后执行_send 绑定 finish 函数,当数据响应实现后,就会触发执行这个 finish 函数
    const finish = FunctionPrototypeBind(onFinish, undefined, this);
    this._send('','latin1', finish);
}

function write_(msg, chunk, encoding, callback, fromEnd) {
  // ...
  len = Buffer.byteLength(chunk, encoding);
  // ...
  if (!msg._header) {if (fromEnd) {msg._contentLength = len;}
  }
  //...
  // 业务代码中调用 res.end,_header 为 null,_implicitHeader 函数在 lib/_http_server.js 中被重写,_implicitHeader 执行会将一个 header+CRLF 赋值给 msg._header
  if (!msg._header) {msg._implicitHeader();
  }
  // ...
    ret = msg._send(chunk, encoding, callback);
  // ...
}

OutgoingMessage.prototype._send = function _send(data, encoding, callback) {if (!this._headerSent) {
    if (typeof data === 'string' &&
        (encoding === 'utf8' || encoding === 'latin1' || !encoding)) {
      // _implicitHeader 函数生成为_header 赋值响应头 +CRLF,因而这里的 data 最终的值为响应头 +CRLF+ 响应体
      data = this._header + data;
    } else {
      const header = this._header;
      ArrayPrototypeUnshift(this.outputData, {
        data: header,
        encoding: 'latin1',
        callback: null
      });
    }
    this._headerSent = true;
  }
  return this._writeRaw(data, encoding, callback);
};

OutgoingMessage.prototype._writeRaw = _writeRaw;
function _writeRaw(data, encoding, callback) {
  const conn = this.socket;
  // ...

  if (conn && conn._httpMessage === this && conn.writable) {
    // ...
    // 将响应的内容增加到响应缓冲区,并写出返回给用户,当写出胜利当前执行回调函数
    return conn.write(data, encoding, callback);
  }
  // ...
}

res.end在执行的时候,次要流程有两个:

  • 调用 write_ 函数,首先会生成响应头,而后将响应头寄存到 _header 中,后续再生成响应内容,将响应内容(响应头 +CRLF+ 响应体)通过 socket 写出响应给用户。
  • 调用 res._send,向socket.write 中写入 finish 回调函数,当服务端的响应内容齐全写出的时候执行 finish 函数,finish函数外部会公布 finish 事件。程序中有两处监听了 finish 事件:

    • parserOnIncoming函数中生成 res 对象后,会在下面监听 finish 事件;
    • res.end函数中订阅了一次 finish 事件,这里的回调函数次要是业务代码调用 res.end 时传入的回调函数。
// 响应头内容解决
// lib/_http_server.js
ServerResponse.prototype._implicitHeader = function _implicitHeader() {this.writeHead(this.statusCode);
};

ServerResponse.prototype.writeHead = writeHead;
function writeHead(statusCode, reason, obj) {
  // ...
  this._storeHeader(statusLine, headers);
  // ...
}

// lib/_http_outgoing.js
OutgoingMessage.prototype._storeHeader = _storeHeader;
function _storeHeader(firstLine, headers) {
  // ...
    this._last = true;
  // ...
  this._header = header + CRLF;
  this._headerSent = false;
  // ...
}

_implicitHeader执行会将响应头 +CRLF 内容寄存到 res._header 中,此时响应头曾经解决完,等到须要应用 socket.write 响应申请的时候,再取出来同响应体一起返回给客户端。

// lib/_http_server.js
function parserOnIncoming(server, socket, state, req, keepAlive) {
  // 留神这里也订阅了 res 对象中的 finish 事件
  res.on('finish',
         FunctionPrototypeBind(resOnFinish, undefined,
                               req, res, socket, state, server));
}

function resOnFinish(req, res, socket, state, server) {
  // 革除 state 中寄存的 req 对象
  ArrayPrototypeShift(state.incoming);
  clearRequestTimeout(req);
  clearIncoming(req);
  // 敞开 res
  process.nextTick(emitCloseNT, res);
  // 敞开 socket 连贯
  if (res._last) {if (typeof socket.destroySoon === 'function') {socket.destroySoon();
    } else {socket.end(); // socket 断开连接
    }
  }
}

function emitCloseNT(self) {
  self.destroyed = true;
  self._closed = true;
  self.emit('close');
}

finish 事件触发,程序会首先将缓冲的 reqres对象删除,而后敞开 socket 连贯,至此这个客户端申请就解决实现了。

正文完
 0