http server源码解析
source link: http://www.cnblogs.com/helloxiaoduan/p/14442557.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
本文主要过下http生成服务和处理请求的主要流程,其他功能并未涉及。
使用例子
const http = require('http'); http.createServer((req, res) => { res.end('hello word'); }).listen(8080);
例子中从生成服务,到接收请求,最后响应请求,其中主要的工作有4部分,分别是:
-
调用
http.createServer
来生成一个服务 -
调用
listen
函数监听端口 -
接收请求,生成
req
和res
对象 -
执行业务函数,执行
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
对象的过程中,会分别监听 request
和 connection
这两个事件。
-
connection
:这里监听的就是net
中的connection
事件,当客户端发起请求,TCP三次握手连接成功时,服务端就会触发connection
事件。connection
事件的回调函数connectionListenerInternal
将在下一个板块进行讲解。 -
request
:当req
和res
对象都初始成功以后,就会发布request
事件,前面代码中我们可以看到request
事件的回调函数requestListener
就是开发者调用http.createServer
时传入的回调函数,这个回调函数会接收req
和res
两个对象。
生成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在进行数据传输的过程中,会将超出缓冲区剩余空间大小的数据进行拆包,使得同一个请求数据包可能分多次发送给服务端。这里 kOnHeaders
和 kOnBody
就是用于拼接被拆分的数据,组合同一个请求的数据。
当请求头解析完成以后,会执行 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;
当 req
和 res
对象都初始成功并存放后,就会执行createServer函数调用传入的业务处理函数。
当 req
生成后,边会执行 parserOnIncoming
生成 res
对象,同时会在 res
对象中注册 finish
事件,当业务代码执行 res.end
的时候,就会触发这个事件。当 req
和 res
对象都准备好后,就会发布 request
事件,同时将 req
和 res
对象传入。 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
事件触发,程序会首先将缓冲的 req
和 res
对象删除,然后关闭 socket
连接,至此这个客户端请求就处理完成了。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK