ryan

Nodejs Http server源码解析

16 次阅读

基于nodejs 创建一个http server很简单:

const http = require('http');
const server = http.createServer((req, res) => {
   if (req.url === '/') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World');
  } else {
    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found');
  }
});
server.listen(3900, () => {
  console.log('Server is running on port 3900');
});

createServer方法定义在lib/http.js文件里:Server的具体实现在/lib/_http_server.js文件里,
function createServer(opts, requestListener) {
  return new Server(opts, requestListener);
}
function Server(options, requestListener) {
  if (!(this instanceof Server)) return new Server(options, requestListener);

  .....

  storeHTTPOptions.call(this, options);

  ....
// 这里可以监听connection事件,是因为Server继承了net.Server
  this.on('connection', connectionListener);
  this.on('listening', setupConnectionsTracking);
  ...
}
// 继承net.Server,源码在lib/net.js中
ObjectSetPrototypeOf(Server.prototype, net.Server.prototype);
ObjectSetPrototypeOf(Server, net.Server);

函数体中storeHTTPOptions的作用:

  1. 配置验证: 验证传入的配置选项是否有效
  2. 默认值设置: 为未提供的选项设置合理的默认值
  3. 安全配置: 设置头部大小限制、超时等安全参数
  4. 性能调优: 配置 keep-alive、连接检查等性能相关参数
  5. 行为控制: 控制 HTTP 解析器的严格程度和头部处理方式

这些配置直接影响 HTTP 服务器的行为、性能和安全性,是服务器初始化的关键步骤。

Server继承自net.Server, net.Server定义了connection事件,当有新的 TCP 连接建立时会自动触发connection.可以理解为这是一种分层设计:net.Server负责TCP连接管理,http.server负责HTTP协议处理. 这也遵循Node.js的事件驱动模式.

当Server收到connection事件的消息时会执行connectionListener,connectionListener实际执行的是connectionListenerInternal,

function connectionListenerInternal(server, socket) {

  socket.server = server;

  if (server.timeout && typeof socket.setTimeout === 'function')
    socket.setTimeout(server.timeout);
  socket.on('timeout', socketOnTimeout);

  // 分配解析器
  const parser = parsers.alloc();

  const lenient = server.insecureHTTPParser === undefined ?
    isLenient() : server.insecureHTTPParser;

  //  初始化解析器
  parser.initialize(
    HTTPParser.REQUEST,
    new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
    server.maxHeaderSize || 0,
    lenient ? kLenientAll : kLenientNone,
    server[kConnections],
  );
  parser.socket = socket;
  socket.parser = parser;

  // Propagate headers limit from server instance to parser
  if (typeof server.maxHeadersCount === 'number') {
    parser.maxHeaderPairs = server.maxHeadersCount << 1;
  }

  const state = {
    onData: null,
    onEnd: null,
    onClose: null,
    onDrain: null,
    ...
  };
   // 绑定函数,socketOnData函数有5个参数,这里先预设4个
  state.onData = socketOnData.bind(undefined,server, socket, parser, state);
  ....  

  // socket接收到数据时,数据作为参数d自动传入.等价于执行socketOnData(server, socket, state, parser, d);                              
  socket.on('data', state.onData);

  // 设置 onIncoming 回调
  parser.onIncoming = parserOnIncoming.bind(undefined,server, socket, state);

  ......
}
// 参数d是包含http请求数据的Buffer
function socketOnData(server, socket, parser, state, d) {
  ...
  // 将原始字节数据传递给HTTP解析器
  const ret = parser.execute(d);
  // 处理解析结果
  onParserExecuteCommon(server, socket, parser, state, ret, d);
}

socket.on(‘data’)的回调函数通过bind的方式预设了server, socket, parser, state等4个参数,d 是动态变化的数据参数,每次调用时由系统根据实际接收到的数据传入。通过bind预设,避免每次调用时重复传递.

当调用 parser.execute(d) 时,实际调用的是c++底层函数,具体流程如下:

在解析HTTP字节流的过程中,解析器会自动调用这些回调,具体处理细节在_http_common.js里面,

const parsers = new FreeList('parsers', 1000, function parsersCb() {
  const parser = new HTTPParser();

  cleanParser(parser);
// 1. 解析到头部字段时
  parser[kOnHeaders] = parserOnHeaders;
// 2. 所有头部解析完成时
  parser[kOnHeadersComplete] = parserOnHeadersComplete;
// 3. 解析到请求体时
  parser[kOnBody] = parserOnBody;
// 4. 整个消息解析完成时
  parser[kOnMessageComplete] = parserOnMessageComplete;

  return parser;
});
具体看所有头部解析完成时的回调parserOnHeadersComplete:


function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
                                 url, statusCode, statusMessage, upgrade,
                                 shouldKeepAlive) {
// this 指向HTTP解析器对象
  const parser = this;
  const { socket } = parser;

  if (headers === undefined) {
    headers = parser._headers;
    parser._headers = [];
  }

  if (url === undefined) {
    url = parser._url;
    parser._url = '';
  }

  // Parser is also used by http client
  const ParserIncomingMessage = (socket?.server?.[kIncomingMessage]) ||
                                 IncomingMessage;
// 创建IncomingMessage对象,这就是req对象
  const incoming = parser.incoming = new ParserIncomingMessage(socket);

// 设置请求对象的各种属性
  incoming.httpVersionMajor = versionMajor;
  incoming.httpVersionMinor = versionMinor;
  incoming.httpVersion = `${versionMajor}.${versionMinor}`;
  incoming.joinDuplicateHeaders = socket?.server?.joinDuplicateHeaders ||
                                  parser.joinDuplicateHeaders;
  incoming.url = url;
  incoming.upgrade = upgrade;

  let n = headers.length;

  // If parser.maxHeaderPairs <= 0 assume that there's no limit.
  if (parser.maxHeaderPairs > 0)
    n = MathMin(n, parser.maxHeaderPairs);

  incoming._addHeaderLines(headers, n);

  if (typeof method === 'number') {
    // server only
    incoming.method = allMethods[method];
  } else {
    // client only
    incoming.statusCode = statusCode;
    incoming.statusMessage = statusMessage;
  }
// 传递incoming,也就是req对象
  return parser.onIncoming(incoming, shouldKeepAlive);
}

当前头部解析完成之后会执行parser.onIncoming回调,parser.onIncoming回调定义在_http.server.js文件connectionListenerInternal函数中

parser.onIncoming = parserOnIncoming.bind(undefined,server, socket, state);

function parserOnIncoming(server, socket, state, req, keepAlive) {
  resetSocketTimeout(server, socket, state);

...
// 这里传入res
  const res = new server[kServerResponse](req,
    .....                                        

  if (!handled) {
   // 传入req与res 通知执行 Server执行 request事件
    server.emit('request', req, res);
  }

  return 0;  // No special treatment.
}

最后用一张图来总结整个流程:

发表评论