1.HTTP协议和TCP协议 #

tcp-http

1.1 长链接 #

long

keepalive

1.2 管线化 #

pipeline

2. URI和URL #

2.1 URI #

URI(Uniform Resource Identifier)是统一资源标识符,在某个规则下能把这个资源独一无二标示出来,比如人的身份证号

2.2 URL #

统一资源定位符,表示资源的地点,URL时使用浏览器访问WEB页面时需要输入的网页地址

2.2.1 URL的格式 #

url

3. HTTP #

3.1 请求报文 #

request

request-header

3.2 响应报文 #

3.3 状态码 #

状态码负责表示客户端请求的返回结果、标记服务器端是否正常、通知出现的错误

3.3.1 状态码类别 #

类别 原因短语
1XX Informational(信息性状态码)
2XX Success(成功状态码)
3XX Redirection(重定向)
4XX Client Error(客户端错误状态码)
5XX Server Error(服务器错误状态吗)

3.3.2 2XX 成功 #

3.3.3 3XX 重定向 #

3.3.4 4XX 客户端错误 #

3.4.5 5XX 服务器端错误 #

4. 首部 #

4.1 通用首部字段 #

首部字段名 说明
Cache-Control 控制缓存行为
Connection 链接的管理
Date 报文日期
Pragma 报文指令
Trailer 报文尾部的首部
Trasfer-Encoding 指定报文主体的传输编码方式
Upgrade 升级为其他协议
Via 代理服务器信息
Warning 错误通知

4.2 请求首部字段 #

首部字段名 说明
Accept 用户代理可处理的媒体类型
Accept-Charset 优先的字符集
Accept-Encoding 优先的编码
Accept-Langulage 优先的语言
Authorization Web认证信息
Expect 期待服务器的特定行为
From 用户的电子邮箱地址
Host 请求资源所在的服务器
If-Match 比较实体标记
If-Modified-Since 比较资源的更新时间
If-None-Match 比较实体标记
If-Range 资源未更新时发送实体Byte的范围请求
If-Unmodified-Since 比较资源的更新时间(和If-Modified-Since相反)
Max-Forwards 最大传输跳数
Proxy-Authorization 代理服务器需要客户端认证
Range 实体字节范围请求
Referer 请求中的URI的原始获取方
TE 传输编码的优先级
User-Agent HTTP客户端程序的信息

4.3 响应首部字段 #

首部字段名 说明
Accept-Ranges 是否接受字节范围
Age 资源的创建时间
ETag 资源的匹配信息
Location 客户端重定向至指定的URI
Proxy-Authenticate 代理服务器对客户端的认证信息
Retry-After 再次发送请求的时机
Server 服务器的信息
Vary 代理服务器缓存的管理信息
www-Authenticate 服务器对客户端的认证

4.4 实体首部字段 #

首部字段名 说明
Allow 资源可支持的HTTP方法
Content-Encoding 实体的编码方式
Content-Language 实体的自然语言
Content-Length 实体的内容大小(字节为单位)
Content-Location 替代对应资源的URI
Content-MD5 实体的报文摘要
Content-Range 实体的位置范围
Content-Type 实体主体的媒体类型
Expires 实体过期时间
Last-Modified 资源的最后修改时间

5.HTTP服务器 #

HTTP全称是超文本传输协议,构建于TCP之上,属于应用层协议。

5.1 创建HTTP服务器 #

let server  = http.createServer([requestListener]);
server.on('request',requestListener);

5.2 启动HTTP服务器 #

server.listen(port,[host],[backlog],[callback]);
server.on('listening',callback);
let http = require('http');
let server = http.createServer(function(req,res){
}).listen(8080,'127.0.0.1',function(){console.log('服务器端开始监听!')});

5.3 关闭HTTP服务器 #

server.close();
server.on('close',function(){});
let http = require('http');
let server = http.createServer(function(req,res){
});
server.on('close',function(){
    console.log('服务器关闭');
});
server.listen(8080,'127.0.0.1',function(){
    console.log('服务器端开始监听!')
    server.close();
});

5.4 监听服务器错误 #

server.on('error',function(){
    if(e.code == 'EADDRINUSE'){
         console.log('端口号已经被占用!);   
    }
});

5.5 connection #

let server = http.createServer(function(req,res){
});
server.on('connection',function(){
    console.log(客户端连接已经建立);
});

5.6 setTimeout #

设置超时时间,超时后不可再复用已经建立的连接,需要发请求需要重新建立连接。默认超时时间时2分钟

server.setTimeout(msecs,callback);
server.on('timeout',function(){
    console.log('连接已经超时');
});

5.7 获取客户端请求信息 #

let http = require('http');
let fs = require('fs');
let server = http.createServer(function(req,res){
  if(req.url != '/favicon.ico'){
    let out = fs.createWriteStream(path.join(__dirname,'request.log'));
    out.write('method='+req.method);
    out.write('url='+req.url);
    out.write('headers='+JSON.stringify(req.headers));
    out.write('httpVersion='+req.httpVersion);
  }
}).listen(8080,'127.0.0.1);
let http = require('http');
let fs = require('fs');
let server = http.createServer(function(req,res){
  let body = [];
  req.on('data',function(data){
    body.push(data);
  });
  req.on('end',function(){
      let result = Buffer.concat(body);
      console.log(result.toString());
  });
}).listen(8080,'127.0.0.1);

5.8 url模块 #

url.parse(urlStr,[parseQueryString]);

5.9 发送服务器响应流 #

http.ServerResponse对象表示响应对象

5.9.1 writeHead #

response.writeHead(statusCode,[reasonPhrase],[headers]);

5.9.2 Header #

设置、获取和删除Header

response.setHeader('Content-Type','text/html;charset=utf-8');
response.getHeader('Content-Type');
response.removeHeader('Content-Type');
response.headersSent 判断响应头是否已经发送

5.9.3 headersSent #

判断响应头是否已经发送

let http = require('http');
let server = http.createServer(function(req,res){
  console.log(resopnse.headersSent?"响应头已经发送":"响应头未发送!");
  res.writeHead(200,'ok');
  console.log(resopnse.headersSent?"响应头已经发送":"响应头未发送!");
});

5.9.4 sendDate #

不发送Date

res.sendDate = false;

5.9.5 write #

可以使用write方法发送响应内容

response.write(chunk,[encoding]);
response.end([chunk],[encoding]);

5.9.6 timeout #

可以使用setTimeout方法设置响应让超时时间,如果在指定时间内不响应,则触发timeout事件

response.setTimeout(msecs,[callback]);
response.on('timeout',callback);

5.9.7 close #

在响应对象的end方法被调用之前,如果连接中断,将触发http.ServerResponse对象的close事件

response.on('close',callback);

模拟http服务 #

let net = require('net');
let server = net.createServer(function(socket){
    parser(socket,function(req,res){
        console.log(req.headers);
        console.log(req.url);
        req.on('data',function(data){
            console.log(data);
        });
        req.on('end',function(){
            res.end(`
HTTP/1.1 200 OK
Content-Length: 2
Content-Type: text/plain

ok`);
        })
    });
});
server.listen(8080);
function parseHeader(str){
    let lines = str.split('\r\n');
    let head = lines.shift();
    let method = head.split(' ')[0];    
    let url = head.split(' ')[1];   
    let httpVersion = head.split(' ')[2].split('/')[2];
    let headers = {}
    console.log(lines)
    lines.forEach(row => {
        row = row.split(': ');
        headers[row[0]] = row[1];
    });
    return {method,url,headers}
}
let {StringDecoder} = require('string_decoder');
let {Readable}  = require('stream');
class IncomingMessage extends Readable{
    _read(){}
}
function parser(socket,callback){
    let buffers = [];
    let sd = new StringDecoder();
    let res = {write:socket.write.bind(socket),end:socket.end.bind(socket)};
    function fn(){
        let content = socket.read();
        buffers.push(content);
        let str = sd.write(Buffer.concat(buffers)); 
        if(str.match(/\r\n\r\n/)){ 
            let result = str.split('\r\n\r\n');
            let header = parseHeader(result[0]);
            let body = result[1];
            let im = new IncomingMessage();
            console.log(im)
            Object.assign(im,header);
            socket.removeListener('readable',fn)
            if(body.length){ 
                socket.unshift(Buffer.from(body));
                socket.on('data',function(data){
                    im.push(data);
                    im.push(null);
                    callback(im,res);
                });
            }else{
                // 请求体没数据
                im.push(null);
                callback(im,res);
            }
        }   
    }
    socket.on('readable',fn);
}

6. HTTP客户端 #

6.1 向其他网站请求数据 #

let req = http.request(options,callback);
req.on('request',callback);
request.write(chunk,[encoding]);
request.end([chunk],[encoding]);
let http = require('http');
let options = {
    hostname: 'localhost',
    port: 8080,
    path: '/',
    method: 'GET'
}
let req = http.request(options, function (res) {
    console.log('状态吗:' + res.statusCode);
    console.log('响应头:' + JSON.stringify(res.headers));
    res.setEncoding('utf8');
    res.on('data', function (chunk) {
        console.log('响应内容', chunk);
    });
});
req.end();

7.querystring #

querystring模块用来转换URL字符串和URL中的查询字符串

7.1 parse方法用来把字符串转换成对象 #

querystring.parse(str,[sep],[eq],[options]);

7.2 stringify方法用来把对象转换成字符串 #

querystring.stringify(obj,[sep],[eq]);