欢迎指导与讨论 : )
序章
本文概要:http.Agent代理、http.ClientRequest客户端请求、http.server服务器、http.ServerResponse服务器相应、http.InComingMessage报文、http.METHODS方法、http.STATUS_CODES状态码、httpCreateServer创建服务端、http.get、http.blobalAgent全局代理、http.request请求。
前言
要使用Http服务器和客户端,我们必须引入 http 模块。大多数http报文的头部都像是下面的这个对象,并且值是不会修改的。
{ 'content-length': '123',
'content-type': 'text/plain',
'connection': 'keep-alive',
'host': 'mysite.com',
'accept': '*/*' }
为了支持所有可能的HTTP应用程序,Node.js的HTTP API是非常低级的。 它仅处理流式数据和报文解析。报文解析的结果会被注入到 headers 和 body
http.Agent
http代理。http代理是为http客户端请求中的sockets连接池而使用的。同时http代理会为客户端请求默认设置 Connection: keep-alive 如果没有等待空闲套接字的http请求,该套接字会被关闭。这意味着,Node在负载下依然能保持活动,且不需要开发人员手动关闭http客户端的keep-alive。
在keep-alive情况下,一个底层会话连接可以多次用于请求,默认情况下通过ClientRequest对象对同一服务器发起的http请求最多可以创建5个连接,即调用http客户端同时对一个服务器发起10次http请求时,实质只有5个请求处于并发状态。
可以设置 agent 为true,选择使用http keep-alive。或设置 agent 为false,脱离连接池的管理,使请求不受并发的限制。当然,连接量过大,会影响服务,因为一个http服务器允许同时连接的套接字数量是有限的。此时我们需要手动关于无用的套接字,即 Connection: 'close'
1) new Agent([ options ])
参数有 keepAlive: Boolean 表示将套接字保留在池中以在日后使用 默认false, keepAliveMsecs :Number 表示一段时间内不发送tcp就会关闭当前套接字 若KeepAlive为true则默认1000, maxSockets: number 表示允许当前host下的最大socket数 默认Infinity, maxFreeSockets: number 表示在空闲状态下,保持打开套接字的最大数量,若KeepAlive为true 默认值为256
// api-1 new http.Agent({})
const http = require('http');
var keepAliveAgent = new http.Agent({ keepAlive: true });
options.agent = keepAliveAgent;
http.request(options, onResponseCallback);
2 ) agent.createConnection( options [, callback ])
参数有 options: object 即一个有详细连接信息的对象, callback: function 参数为( err, stream ),且返回一个套接字。
3 ) agent.destroy( )
销毁当前代理中任何正在使用的套接字。通常不需要这样做。但是,如果我们使用启用了KeepAlive的代理,则在知道它将不再使用时显式关闭代理。 否则,套接字可能会在服务器终止它们之前挂起很长时间。
4 ) agent.freeSockets
当使用http keep-alive时,返回含当前等待代理使用套接字数组的对象。不要修改
5 ) agent.getName( options )
option含有 host: string 发出请求的服务器的域名或IP地址, port: number 远程服务器端口, localAddress: string 发出请求时用于网络连接的本地接口
此api获取一组请求选项的唯一名称,以确定是否可以重新使用连接。
6 ) agent.maxFreeSockets 请参照上文
7 ) agent.maxSpckets 请参照上文
8 ) agent.requests 返回包含尚未分配给套接字的请求队列的对象,不要修改。
9 ) agent.sockets 返回包含代理程序当前正在使用的套接字数组的对象,不要修改。
http.ClientRequest
http客户端请求。该对象会在 http.request( ) 返回。它表示一个正在进行中的请求,而且该请求的头部已经被序列化。我们依然可以通过 setHeader getHeader removeHeader 来改变头部的信息。头部的信息会和第一个发送数据块或者即将关闭网络连接的信息一起发送。
我们可以通过为 request 对象添加一个名为 response 的监听器,来获得返回信息。该监听器会在返回信息到达时,被request对象触发。 response 事件会接受一个 http.IncomingMessage 实例的参数。
在 response 事件发生时,我们可以为 response 对象添加一个名为 data 的事件,来获取数据块。同时,若我们没有定义response事件发生时的处理函数,该response会被丢弃。
值得注意的是,若我们添加了一个response事件的处理函数,我们必须从response对象中读取数据,无论是通过 response.read( ) 来获取可读数据,或者是添加 data 事件,又或者是调用 resume 方法。直到数据被读取完, end 事件才会被触发。并且,当数据读取完毕前,它会一直消耗着内存,这有可能最终会报出一个'内存不足'的错误。(注:Node不会检查 Content-Length 和已传输报文体的大小是否相等)
因为request对象implements了'可写流'的接口,因此会有以下的这一些事件钩子:
1)Event: 'abort' function ( ) { } 中止事件。该事件会在请求被客户端中止时触发。(注:浏览器中,可以通过调用XMLHttpRequest对象的abort方法来取消正在进行的http请求 )
2)Event: 'checkExpectation' function ( request, response ) { } 当每次受到带有 Expect 的http请求头部,同时该值不为 100-continue 时,该事件会被触发。若无指定该事件和对应的处理函数,服务器将自动响应417。 注意,当此事件被触发后, request 事件就不会被触发了
3)Event: 'connect' function ( response, socket, head ) { } 。每次服务器使用 CONNECT 方法响应请求时,该事件会被触发。若服务器未添加该事件,客户端将关闭该连接。
var options = {
port: 1337,
hostname: '127.0.0.1',
method: 'CONNECT',
path: 'www.google.com:80'
};
var req = http.request(options);
req.on('connect', (res, socket, head) => {
console.log('got connected!');
})
4)Event: 'continue' function ( ) { } 当服务端返回 '100 Continue' 的相应时触发
5)Event: 'response' function ( response ) { } 客户端接受到服务器的响应时触发。该方法之后被触发一次,且方法内的 response 参数是 http.IncomingMessaeg 的一个实例
6)Event: 'socket' function ( socket ) { } 当一个套接字分配给该请求时发出
7)Event: 'upgrade' function ( response, scoket , head ) { } 每当服务器返回'upgrade'相应时发出。若无添加该事件的监听和处理函数,该连接会被中断。
var options = {
port: 1337,
hostname: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
}
};
var req = http.request(options);
req.on('upgrade', (res, socket, upgradeHead) => {
console.log('got upgraded!');
})
同时,request请求对象会具备以下的这些方法
1)request.abort( ) 中止请求。响应中的数据会被丢弃,同时解除已连接的套接字。
2)request.end( [data][, encode][, callback] ) 结束发送的请求。
3)request.setNoDelay([ noDelay ]) 每当套接字被分配到当前请求时,socket.setNoDelay( )会被调用。
4)request.flushHeaders( ) 刷新请求头。出于效率原因,Node会一般都会缓存请求头,直到我们调用 request.end( ) 或者编写第一个request数据块。然后Node会尽力把请求头和数据打包到单个TCP数据包中。我们可以调用request.flushHeader( )来绕过优化并启动请求。
5)request.setSocketKeepAlive( [enable] [, initialDelay] ) 一旦套接字被连接,socket.setKeepAlive( )会被调用。
6)request.setTimeout( timeout [, callback] )一旦套接字被连接,socket.setTimeout( )会被调用。
7)request.write( chunk [, encoding] [, callback]) 发送请求体的一个数据块。通过调用该api,我们能以流的形式将数据传输到服务器。encoding参数默认为'utf8',而callack会在时数据块刷新时被调用。
http.server
该类是继承自'net.Server',同时会具备以下的异步事件
1)Event: 'checkContinue'。每当收到带有 Expect: 100 - continue 的http请求头时触发。若我们没有监听该事件并设置相应的处理函数,服务器会自动响应 100 Continue 当该事件被触发时, request 事件不会被触发
2)Event: 'checkExpectation'。每当收到带有 Expect 的http请求且该值不是 100-continue 时被触发。若没有监听该事件和设置对应的处理函数,服务器会自动返回 417。当该事件被触发时,request事件不会被触发。
3)Event: 'clientError' function ( err, socket ) { } 当客户端连接发出'错误'事件时被触发。此事件监听器的处理函数可以负责关闭底层的套接字。且当该事件发生时,回调函数就不存在request和reponse对象了。因此想发送任何的http相应和其他内容都必须直接写在套接字对象中。
const http = require('http'); const server = http.createServer((req, res) => {
res.end();
});
server.on('clientError', (err, socket) => {
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(8000);
4)Event: 'close' 当服务器关闭时触发
5)Event: 'connect' function ( request, socket, head ) { } 。每当收到带有 method: CONNECT 的http请求时被触发。若没有监听该事件,客户端连接会被关闭。
6)Event: 'connection' function ( socket ) { } 每当一个新的TCP流被建立时触发。
7)Event: 'request' function ( request, response ) { } 每当有请求到达时被触发。注:每个连接可以有多个请求( keep-alive connections )。
8)Event: 'upgrade' function ( request, socket, head ) { } 每当带有upgrade的http请求到达时被触发,若没有设置该监听器,客户端连接会被关闭。
http.server也带有以下方法
1)server.close( ) 让服务器停止接受新的连接。
2)server.listen( handle [, callback]) handle可以是一个套接字或者是一个服务器对象。服务器会按照指定的handle来接受连接。
3)server.listen( path [, callback])为指定的path开启连接监听
4)server.listen( port [, hostname] [, backlog] [, callback])
http.server也带有以下属性
1)server.listening: Boolean 服务器是否在监听状态
2)server.maxHeadersCount 限制请求头部的最大数量。默认为1000
3)server.setTimeout( msecs, callback )设置套接字的超时值,并主动触发'timeout'事件。若发生超时,则将套接字作为参数传递。超时值默认为2分钟,在超时发生时,套接字将自动销毁。
4)server.timeout 返回设置的超时时间,默认为2分钟。
http.ServerResponse
该对象在Http服务器内部被创建,并作为第二个参数传递到request事件。同时该对象implements自可写流接口。作为一个EventEmitter,它具有以下的这一些事件。
1)Event: 'close' 表示底层连接已经中断。
2)Event: 'finish' 每当响应被发送时被触发。但这不意味着客户端已经接受到任何响应。
http.ServerResponse的一些方法和属性
1)response.addTrailers( headers ) 该方法为响应头的尾部继续加入其他信息。同时,尾部信息只有在编码合乎响应时才会被发送,若不合适则会被默默丢弃。
response.writeHead(200, { 'Content-Type': 'text/plain',
'Trailer': 'Content-MD5' });
response.write(fileData);
response.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});
response.end();
2)response.end( data [, encoing] [, callback]) 该方法表明所有相应的头部和体部信息都已经发送完毕。注:response.end( ) 必须在每一个相应结束时调用,不然客户端会一直等待。
3)response.finished 返回响应是否已经全部完成
4)response.getHeader( name ) 读取一部分还没有发送到客户端的头部信息
var contentType = response.getHeader('content-type');
5)response.headersSent 返回响应头是否已经发送
6)response.removeHeader( name ) 移除等待发送头部的某个键值对
7)response.sendDate 设置是否自动马上 Date 的头部信息,默认为 true。Http也要求响应头部中存在 Date 信息
8)response.setHeader( name, value ) 设置头部信息的键值对。若该键值和它的值已经存在,则把新值覆盖旧值。同时由该api设置的头部信息会被合并到 response.writeHead( )的信息中。
response.setHeader('Content-Type', 'text/html');
response.setHeader('Set-Cookie', ['type=ninja', 'name=asd']);
9)response.setTimeout( msecs, callback ) 设置套接字的超时时间。若也提供了callback函数,则相当于为response对象添加'timeout'监听器。若无监听'timeout'监听器,则套接字会在超时时自动销毁。
10)response.statusCode 设置http状态码。 response.statusCode = 404;
11)response.statusMessage 设置状态码对应的状态信息。若设置为'undefined',则自动使用标准的状态信息。
12)response.write( chunk [, encoding] [, callback ]) 该api能调用多次。当第一次调用时,它会发送缓存的头部信息和第一个相应体给客户端。第二次调用时,Node会认为你是要传输流式数据,并缓存到第一块相应体中。
13)response.writeContinue( ) 向客户端发送HTTP / 1.1 100 Continue消息,指示应该发送请求体。
14)response.writeHead( statusCode, [, statusMessage ] [, headers ]) 发送相应头到请求中。此api只能被调用一次,而且只能在response.end( ) 之前调用。若在该api之前调用response.setHeader( ),他们会被合并到response.writeHead( )已存在的头部键值对中。
var body = 'hello world';
response.writeHead(200, {
'Content-Length': Buffer.byteLength(body),
'Content-Type': 'text/plain' });
// returns content-type = text/plain
const server = http.createServer((req,res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader('X-Foo', 'bar');
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('ok');
});
http.InComingMessage
InComingMessage对象由http.server或者http.ClientRequest对象创建。它实现了可读流的接口,具有以下事件、方法和属性
1)Event: 'aborted' 当客户端中断请求和套接字关闭时触发
2)Event: 'close' 当底层的连接断开时触发,该事件在每个响应里只触发一次。
3)message.destroy( [error ]) 在接受到IncomingMessage的套接字上调用。
4)message.headers 返回请求/响应的头部信息对象。
// { 'user-agent': 'curl/7.22.0',
// host: '127.0.0.1:8000',
// accept: '*/*' }
console.log(request.headers);
5)message.httpVersion 返回Http版本。
6)message.method 返回请求的方法
7)message.rawHeaders 返回响应/请求的头部信息<Array>。
8)message.setTimeout( msecs, callback )
9)message.statusCode 返回状态码
10) message.statusMessage 返回状态信息
11)message.socket 返回与连接相关的套接字对象
12)message.url 返回请求的指定地址
http.METHODS
返回一组Http能支持解析的Http方法
http.STATUS_CODES
返回一组http状态码与默认信息的对象
httpCreateServer
返回一个新的http.Server实例
http.get
Node提供的一个便捷方式,method自动设为'GET',并自动调用req.end( )
http.get('http://nodejs.org/dist/index.json', (res) => {
const statusCode = res.statusCode;
const contentType = res.headers['content-type'];
// ...
})
http.blobalAgent
http.request
http.request( options [, callback ]) { } 其中option有以下这些选项:protocol 默认为'http';host 默认为 'localhost';hostname;family;port 默认为 80;localAddress;socketPath;method;path;headers;agent;Connection;timeout;等
var postData = querystring.stringify({
'msg' : 'Hello World!'
}); var options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
}; var req = http.request(options, (res) => {
// ...
})