HTTP的模块是nodejs最重要的模块(应该是),最近在看HTTP权威指南,重新过了一遍http协议和web客户端。再来看这个http。
HTTP构建于TCP之上,属于应用层协议,继承自tcp服务器(net模块),它能与多个客户端保持连接,由于基于事件驱动,并不为每个连接创建额外的进程或线程,所以能实现高并发;HTTP服务器与TCP服务器有区别的地方在于,在开启keepalive后,一个tcp会话可以用于多次请求和响应。TCP服务是以connection为单位进行服务,HTTP是以request为单位进行服务,HTTP模块就是将connection到request的过程进行了封装。
http模块将连接所用的套接字的读写抽象为serverRequest和serverResponse对象,他们分别对应请求和响应。在请求产生过程中,http模块拿到连接中的数据,调用二进制模块http_parser进行解析,解析玩报文的报头后,触发request事件,调用用户的业务逻辑。
HTTP的消息头通过对象表示
{ 'content-length': '123',
'content-type': 'text/plain',
'connection': 'keep-alive',
'host': 'mysite.com',
'accept': '*/*' }
http.STATUS_CODES//例如http.STATUS_CODES[404]==='Not Found';
http.createServer([requestListener]);//返回一个新的web服务器对象;
类http.Server这是一个包含下列事件的EventEmitter
事件'request'function(request,response){}
//每次收到一个请求时触发,每个链接又可能有多个请求;
//request是http.IncomingMessage的一个实例.response是http.ServerResponse的一个实例;
事件'connection' function(socket){}
//新的TCP流建立时触发。socket是一个net.Socket对象。通常用户无需处理改时间
事件'close'
事件'checkContinue'//每次收到Expect:100-continue的http请求时触发。如果未监听该事件,服务器会酌情发送100Continue响应
事件'connect' function(request,socket,head){}//每次客户端发起CONNECT请求时触发。如果未监听该事件,客户端发起CONNECT请求时连接会被关闭
//request是该http请求的参数;head是一个Buffer实例,隧道流的第一个包,可能为空
//这个世界被分发后,请求的套接字将不会有data事件监听器,你需要绑定一个监听器到data事件,处理套接字被发送到服务器的数据
事件'upgrade' function(request,socket,head){}//每当一个客户端请求http升级时触发;需要绑定data监听器
事件'clientError'function(exception,socket){}//客户端触发一个error事件,被转发到此;
server.listen(port,[hostname],[cb],[backlog],[callback])//开始在指定的主机名和端口接收连接
//积压量backlog为连接等待队列的的最大长度。
server.listen(path,[cb]);//启动一个unix套接字在所给路径path上监听连接;
server.close([cb])//仅在服务端接收新连接
server.maxHeadersCount//最大请求头目现在,默认1000个
server.setTimeout(msecs,cb);//设置超时值,若超时,分发’timeout‘事件,同时将套接字做参传入;
server.timeout//一个套接字被判为超时之时闲置的毫秒数;
类http.ServerResponse//这是一个由HTTP服务器内部创建的对象,他将做为2参传递到'request'事件中;实现了weitable Stream接口,是一个包含下列事件的EventEmitter
事件'close' function(){}//底层连接在response.end()或被调用或可以冲洗掉之前就被终结了。;
response.writeContinue()//发送一个HTTP/1.1 100 Continue消息至客户端,表明请求体可被发送;
response.writeHead(statusCode,[reasonPhrase],[headers])//向请求恢复响应头,statusCode是一个3位HTTP状态码;
//headers是响应头的内容。把人类可读的‘原因短句’作为第二个参数;
response.setTimeout(msecs,cb);
response.statusCode//使用默认headers时,这个属性决定headers更新时被传回客户端的http状态码;
response.setHeader(name,value)//为默认或已存在的头设置一条单独的头内容,如果已存在于将被送出的头重,将会覆盖其如果想设置更多的头,就会使用一个相同名字的字符串数组。
·response.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);
·response.setHeader("Content-Type","text/html");
response.headersSent//如果headers发送完毕为true,反正为false;
response.sendData//若为true,则当headers里没有Date值时自动生成Date并发送,默认true;
response.getHeader(name)//读取一个在队列中但还没有被发送只客户端的header
response.removeHeader(name)//取消一个在队列内等待发送的header
response.write(chunk,[encoding])//发送一个响应体的数据块,可能被调用多次以防继承部分响应体;
//chunk可以是字符串或缓存,若为字符串,2参表明如何将一个字符串编码为一个比特流。
response.addTrailers(headers)//这个方法添加HTTP尾随headers给响应;
//只有当数据块编码被用于响应时尾随才会被触发,如果不是,会被自动丢弃;
//如果触发尾随消息,HTTP要求一个报文头场列表和Trailer报头一起发送;
response.writeHead(200, { 'Content-Type': 'text/plain',
'Trailer': 'Content-MD5' });
response.write(fileData);
response.addTrailers({'Content-MD5': "7895bf4b8828b55ceaf47747b4bca667"});
response.end();
response.end([data],[encoding]);//当所有响应报头和报文被发送完成时,这个方法将信号发送给服务器,服务器会认为消息完成了,每次响应完成必须调用此;
http.request(options,cb);
//node维护几个连接每个服务器的HTTP请求,这个函数允许后台发布请求。
//options可以是一个对象或一个字符串。如果options是一个字符串,它将自动使用url.parse()解析;
host:请求发送到的服务器的域名或IP地址。默认为'localhost'。
hostname:用于支持url.parse()。hostname比host更好一些
port:远程服务器的端口。默认值为80。
localAddress:用于绑定网络连接的本地接口。
socketPath:Unix域套接字(使用host:port或socketPath)
method:指定HTTP请求方法的字符串。默认为'GET'。
path:请求路径。默认为'/'。如果有查询字符串,则需要包含。例如'/index.html?page=12'。请求路径包含非法字符时抛出异常。目前,只否决空格,不过在未来可能改变。
headers:包含请求头的对象。
auth:用于计算认证头的基本认证,即'user:password'
agent:控制Agent的行为。当使用了一个Agent的时候,请求将默认为Connection: keep-alive。可能的值为:
undefined(默认):在这个主机和端口上使用[全局Agent][]。
此例构造了一个http客户端,node模拟的客户端请求。
var options={ hostname:‘127.0.0.1’, port:1334, path:'/', method:'GET' } var req=http.request(options,function(res){ console.log('STAUS: '+res.statusCode); console.log('HRADERS: '+JSON.stringify(res.headers)); res.setEncoding(utf8); res.on('data',function(chunk){ console.log(chunk); }) }) req.end()
Agent对象:在Agent中显式使用passed。
false:在对Agent进行资源池的时候,选择停用连接,默认请求为:Connection: close。
keepAlive:{Boolean} 保持资源池周围的套接字在未来被用于其它请求。默认值为false
keepAliveMsecs:{Integer} 当使用HTTP KeepAlive的时候,通过正在保持活动的套接字发送TCP KeepAlive包的频繁程度。默认值为1000。仅当keepAlive被设置为true时才相关。
//http.request()返回一个http.ClientRequest类的实例。ClientRequest实例是一个可写流对象,如果需要用post请求上传一个文件的话,将其写入到ClientRequest对象;
http.get(options,cb)//与http.request()唯一区别是它设置的是get方法并自动调用req.end();
http.get("http://www.google.com/index.html", function(res) { console.log("响应:" + res.statusCode); }).on('error', function(e) { console.log("错误:" + e.message); });
类http.Agent
//用于把套接字做成资源池,用于http客户端请求;
http服务器默认的客户端代理对象http.globalAgent,它对每个服务器端创建的连接进行管理,默认情况下,对同一个服务器端发起的http请求最多创建5个链接,它的实质是一个连接池。
我们可以自行构造代理对象。
var agent=new http.Agent({ maxSockets:10 }) var options={ hostname:"127.0.0.1", post:1334, path:"/", method:"GET", agent:agent };
也可以设置agent为false,就脱离连接池的管理,使请求不受并发的限制。
http Agent把客户端请求默认使用Connection:keep-alive。如果没有http请求在等待成为空闲的套接字,那么套接字将关闭。
当套接字触发了close事件或特殊的agentRemove事件的时候,套接字从agent资源池中移出。
如果你打算保持一个http请求长时间开启,并不希望它保持在资源池中,那么你可以按照下列代码:
http.get(options,function(res){ //..dosomething }).on('socket',function(socket){ socket.emit('agentremove') }) 可用agent:false选择完全停用资源池; http.get({ hostname:'localhost', port:80,path:'/',agent:false, },function(res){//..})
类http.ClientRequest
//该对象在内部创建,并由http.request()返回。他表示着一个正在处理的请求,其头部已经进入请求队列。
//该头部仍可以通过setHeader(name,value),getHeader(name),removeHeader(name)等api修改;
//为了获取响应对象,给请求对象添加一个'response'监听器。当接收到响应头时,请求对象会触发'response',该事件执行时有一个参数,参数为http.IncomingMessage的一个实例
在'response'事件期间,可以为响应对象添加监听器,尤其是'data事件'。
Event 'response'
function(res){}//当接收到请求的响应时触发,只被触发一次。response参数是http.http.IncomingMessage的一个实例;
事件'socket'//触发于一个套接字被赋予为这个请求的时候
事件'connect'//每次服务器使用connect方法响应一个请求时被触发。如果该事件未被监听,接收connect方法的客户端将关闭它们的连接;
socket.write('GET /HTTP/1.1\r\n'+'Host:www.google.com:80\r\n'+'Connection:close\r\n'+'\r\n'); socket.on('data',function(chunk){ console.log(chunk.toString()); }) socket.on('end',function(){ proxy.close(); })
事件'upgrade' function(response,socket,head){}
//每次服务器返回upgrade响应时触发。如果事件为被监听,客户端收到upgrade后将关闭连接
req.on('upgrade',function(res,socket,upgradeHead){ console.log('got upgrade!'); socket.end(); process.exit(0); })
事件'continue'
//当服务器发送100 continue响应时触发,通常是因为请求保护Expect:100-contine,该指令表示客户端应发送请求体。
request.write(chunk,[encoding])
//发送一块请求体。调用多次。用户可以流式发送请求体至服务器--在这种情况下,创建请求时建议使用['Transfer-Encoding','chunked']当所有响应报头和报文被发送完成时,这个方法将信号发送给服务器,服务器会认为消息完成了,每次响应完成必须调用此;
request.end([data],[encoding])
//结束发送请求。
request.abort()//终止一个请求;
request.setTimeout(timeout,[cb])//一旦一个套接字被分配给该请求并且完成连接,socket.setTimeout()将被调用
request.setNoDelay([noDelay])//一旦一个套接字被分配到一个请求 同上
http.http.IncomingMessage; 一个IncomingMessage对象由http.server或者http.http.ClientRequest创建的。并作为第一参数分别传递给'request'和'response'事件。它也可以被用来访问应答的状态,头文件和数据。’
事件'close'表示在response.end()被掉用或强制刷新之前,底层的连接已经被终止了。