首先需要 require('http')。
- 1、为了支持各种可能的HTTP应用,Node.js的HTTP API是非常底层的,它只涉及流处理与信息解析。它把一个信息解析成消息头和消息主体
- 2、rawHeaders属性中,它是一个[key,value,key2,value2... ]
Agent类
Agent负责为HTTP客户端管理连接的持续与复用。它为一个给定的主机与端口维护这个一个等待请求的队列,且为每个请求重复使用一个单一的socket链接直到队列为空,此时socket会被销毁或被放入一个连接池中,在连接池中带带被有着相同的主机与端口的请求再次使用。
连接池中的连接的TCP keep-alive是开启的。但是服务器仍然可能关闭闲置的连接,在这种情况下,这些链接会被移出连接池,且当一个新的HTTP 请求被创建时再为指定的主机与端口创建一个新的链接,服务器也可能拒绝允许同一连接上有多个请求,这种情况下,连接会为每个请求重新创建,且不能被放入连接池。Agent仍然会创建请求道服务器,但每个请求会出现在一个新的连接。
如果有一个连接被服务端或者客户端关闭时,它会被移出连接池,连接池中任何未被使用的socket会被释放,从而是Nodejs进程在没有请求时不用保持运行
当Agent实例不在被使用时,建议destroy()它,因为未被使用的socket也会消耗操作系统资源
http.get(options,(res) =>{
// todo
}).on('socket',(socket) => {
socket.emit('agentRemove')
})
代理也可被用于单独的请求。使用{agent:false}作为http.get()或者http.request函数的选项,则会为客户端连接创建一个默认配置的一次性使用的agent
new Agent([options])
- options 代理的配置选项
- keepAlive <boolean> 保持socket可用即使没有请求,为后面的请求使用,而不是重新建立TCP链接
- keepAlive<number> 当使用了keepAlive选项时,该选项指定TCP keep-alive数据包的初始化延迟,当keepAlive选项为false或者undefined时。改选项无效,默认为1000
- maxSockets<number> 每个主机允许的最大sockets数量。默认为Infinity
- maxFreeSockets<number> 在空闲状态下允许打开的最大socket数值,仅当keepAlive为true时才有效,默认值为256.
- timeout<number> socket超时毫秒数,socket连接建立之后超时时间(指客户端从服务端读取数据的超时时间),这将在套接字建立连接后设置超时。
** http.request()使用的默认http.globalAgent的选项均为各自的默认值**
若要配置其中任何一个,则需要创建自定义的http.agent实例
const http = require('http')
const keepAliveAgent = new http.Agent({keepAlive:true})
option.agent = keepAliveAgent
http.request(options,onResponseCallback)
agent.createConnection(option[,callback])
- options 包含连接详情的选项,查看
net.createConnection()
- callback接收被创建的socket的回调函数
- 返回
net.socket
创建一个用于http请求的socket或流。
默认情况下,该函数类似于net.createConnection(),但是如果期
望更大的灵活性,自定义的代理可以重写该方法。
socket或流可以通过以下两种方式获取,从该函数返回,或传入callback。[callback(err,stream)]
socket keepSocketAlive(socket)
在socket
被请求分离的时候调用,可能被代理持续使用
socket.setKeepAlive(true,this.keepAliveMsec)
socket.unref()
return true
这个方法可以被一个特定的Agent子类重写,如果这个方法返回假值,socket会被小会而不是在下一次请求时候持续使用
agent.reuseSocket(socket,request)
由于keep-alive
选项被保持持久化,在socket附加到request时候调用。默认行为是:
socket.ref()
agent.destory()
销毁当前在被代理使用的任何socket。
agent.freeSockets
返回一个对象,包含当前正在等待被启用了keepAlive的代理使用的socket的数组
agent.getName(options)
- options 为名称生成程序提供信息的选项
- host 请求发送至的服务器的域名或ip地址
- port 远程服务器的端口
- localAddress 当发送请求时,为网络链接绑定本地接口
agent.maxFreeSockets
默认为256,对于已启用keepAlive的代理,该属性可设置要保留的空闲socket的最大数量
agent.maxSockets
默认rewind不限制,该属性可设置代理为每个来源打开的并发socket的最大数量,来源是agent.getName()的返回值
agent.requests
返回一个对象,包含还未被分配到socket的请求队列
agent.sockets
返回一个对象,包含当前正被代理的socket数组
http.clientRequest类
该对象在http.request内部被创建并返回。它表示一个正在处理的请求,其请求头已进入队列。请求头仍可使用setHeader(name,value)、getHeader(name)和removeHeader(name) API进行修改。实际的请求头会与第一个数据块一起发送活当调用request.end()时发送。
要获取响应,需要为response
事件添加一个监听器到请求对象上,当响应头被接收到时,‘response’事件会从请求对象上被触发。response
事件被执行时,带一个参数,该参数是一个http.IncomingMessage实例。
在response
事件期间,可以添加监听器到相应对象上,比如监听‘data’事件
如果没有添加response
事件处理函数,则响应会被整个丢弃。如果添加了response
事件处理函数,则必须消耗完响应对象的数据,可通过调用response.read()
、或者添加一个data
事件处理函数、或调用resume
方法,数据被消耗完时,会触发end()
事件。在数据被读取玩之前会消耗内存,可能会造成process out of memory
错误
注意:node.js 不会检查Content-Length
与已传输的请求主体的长度是否相等
const http = require('http')
const net = require('net')
const url = require('url')
// 创建一个HTTP 代理服务器
const proxy = http.createServer((req,res) => {
res.writeHead(200,{'Content-type':'text/plain'})
res.end()
})
proxy.on('connect',(req,cltSocket,head) => {
// 连接一个服务器
const srvUrl = url.parse(`http://${req.url}`)
const srvSocket = net.connect(srvUrl.port,srvUrl.hostname,
() =>{
cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node.js-Proxy\r\n' +
'\r\n');
srvSocket.write(head)
srvSocket.pipe(cltSocket)
cltSocket.pipe(srvSocket)
})
})
proxy.listen(13006,'127.0.0.1',() => {
// 发送一个请求到代理服务器
const options = {
port:1337,
hostname:'127.0.0.1',
method:'CONNECT',
path:'www.makan.com:80'
}
const req = http.request(options)
req.end()
req.on('connect',(res,socket,head) => {
console.log('已连接')
socket.write('GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n')
socket.on('data', (chunk) => {
console.log(chunk.toString());
});
socket.on('end', () => {
proxy.close();
});
})
})
request.write(chunk[,encoding[,callback]])
发送请求体的一个数据块,通过多次调用该方法,一个请求主体可被发送到一个服务器,在这种情况下,,当穿件请求时,建议使用['Transfer-Encoding','chuned']请求头
upgrade事件
-
request
http请求,同request
事件 -
socket
服务器与客户端之间的网络socket -
head
流的第一个数据包,可能为空
每当客户端发送httpupgrade
请求时触发,如果该事件未被监听,则发送upgrade
请求的客户端会关闭连接。
当该事件被触发后,请求的socket上没有‘data’事件监听器,这意味着需要绑定‘data’事件监听器。用来处理socket上被发送到服务器的数据
http.ServerResponse类
该对象在http服务器内部被创建。它作为第二个参数被传入'request'事件。
这个类实现了可读流接口
response.addTrailers(headers)
该方法会添加HTTP尾部响应头(一种在消息尾部的响应头)到相应
仅当相应使用分块编码时,尾部响应才会被发送,否则(不如请求为 HTTP/1.0),尾部响应头会被丢弃
**注意,发送尾部响应头之前,需要先发送Trailer
响应头,并在值里带上尾部相应头字段的列表
response.writeHead(200, { 'Content-Type': 'text/plain',
'Trailer': 'Content-MD5' });
response.write(fileData);
response.addTrailers({ 'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667' });
response.end();
response.write
response.write()
首次被调用时,会发送缓冲的响应头信息和响应主体的第一块数据到客户端。 response.write()
第二次被调用时,Node.js 能够确定数据会被接收,于是开始传输新数据。 也就是说,响应的完成取决于响应主体的第一块数据。
Buffer.byteLength()
http.IncomingMessage
IncomingMessage
对象是由http.Server
或http.ClientRequest
创建,并作为第一个参数分别传递给request
和response
事件。他可以用来访问响应状态,消息头以及数据。
它实现可读流接口
message.headers
请求头或响应头的对象
头信息的名称与值的建制度,头信息的名称为小写
// { 'user-agent': 'curl/7.22.0',
// host: '127.0.0.1:8000',
// accept: '*/*' }
console.log(request.headers);
原始头信息中的重复数据会按以下方式根据头信息名称进行处理
- 重复的
age
、authorization
、content-length
、content-type
、etag
、expires
、from
、host
、if-modified-since
、if-unmodified-since
、last-modified
、location
、max-forwards
、proxy-authorization
、referer
、retry-after
、或user-agent
会被丢弃。 -
set-cookie
始终是一个数组。重复的会被添加到数组 - 对于其他头信息,其值使用 , 拼接。
http.METHODS
返回解析器支持的HTTP 方法的列表
http.createServer([options][,requestListener])
http.get
与http.request()唯一的区别就是它设置请求方法为GET且自动调用req.end()
http.get('http://nodejs.org/dist/index.json', (res) => {
const { statusCode } = res;
const contentType = res.headers['content-type'];
let error;
if (statusCode !== 200) {
error = new Error('请求失败。\n' +
`状态码: ${statusCode}`);
} else if (!/^application\/json/.test(contentType)) {
error = new Error('无效的 content-type.\n' +
`期望 application/json 但获取的是 ${contentType}`);
}
if (error) {
console.error(error.message);
// 消耗响应数据以释放内存
res.resume();
return;
}
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
console.log(parsedData);
} catch (e) {
console.error(e.message);
}
});
}).on('error', (e) => {
console.error(`错误: ${e.message}`);
});
http.request(options[,callback])
Node.js为每台服务器维护多个连接来进行HTTP请求,该函数允许显示地发出请求。
options可以是一个对象,字符串 或者URL对象, 如果 options
是一个字符串,它会被自动使用 url.parse()
解析。 如果它是一个 URL
对象, 它会被默认转换成一个 options
对象。
可选的 callback
参数会作为单次监听器被添加到 'response'
事件。
http.request()
返回一个 http.ClientRequest
类的实例。 ClientRequest
实例是一个可写流。 如果需要通过 POST 请求上传一个文件,则写入到 ClientRequest
对象。
例子:
const postData = querystring.stringify({
'msg' : 'Hello World!'
});
const options = {
hostname: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
const req = http.request(options, (res) => {
console.log(`状态码: ${res.statusCode}`);
console.log(`响应头: ${JSON.stringify(res.headers)}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应主体: ${chunk}`);
});
res.on('end', () => {
console.log('响应中已无数据。');
});
});
req.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
// 写入数据到请求主体
req.write(postData);
req.end();
注意 在例子中调用了req.end()。使用http.request()必须要调用req.end()
以下是需要注意的几个特殊的请求头
发送 'Connection: keep-alive' 会通知 Node.js,服务器的连接应一直持续到下一个请求。
发送 'Content-Length' 请求头会禁用默认的块编码。
发送 'Expect' 请求头会立即发送请求头。 通常情况下,当发送 'Expect: 100-continue' 时,超时时间与 continue 事件的监听器都需要被设置。 详见 RFC2616 章节 8.2.3。
发送 Authorization 请求头会替代 auth 选项计算基本身份验证。
URL的实例:
const { URL } = require('url');
const options = new URL('http://abc:xyz@example.com');
const req = http.request(options, (res) => {
// ...
});