node之http模块

首先需要 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事件

  • requesthttp请求,同request事件
  • socket服务器与客户端之间的网络socket
  • head流的第一个数据包,可能为空
    每当客户端发送http upgrade请求时触发,如果该事件未被监听,则发送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.Serverhttp.ClientRequest创建,并作为第一个参数分别传递给requestresponse事件。他可以用来访问响应状态,消息头以及数据。
它实现可读流接口

message.headers

请求头或响应头的对象
头信息的名称与值的建制度,头信息的名称为小写

// { 'user-agent': 'curl/7.22.0',
//   host: '127.0.0.1:8000',
//   accept: '*/*' }
console.log(request.headers);

原始头信息中的重复数据会按以下方式根据头信息名称进行处理

  • 重复的 ageauthorizationcontent-lengthcontent-typeetagexpiresfromhostif-modified-sinceif-unmodified-sincelast-modifiedlocationmax-forwardsproxy-authorizationrefererretry-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) => {
  // ...
});

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容