重学前端六---HTTP请求

388 阅读2分钟

一、HTTP的协议解析

1、ISO-OSI七层网络模型

  • 应用层

    网络服务与最终用户的一个接口。 协议有:HTTP FTP

  • 表示层 (五层模型应用层)

    数据的表示、安全、压缩。 格式有,JPEG、ASCll、EBCDIC、加密格式等

  • 会话层(五层模型应用层)

    建立、管理、终止会话。 对应主机进程,指本地主机与远程主机正在进行的会话

  • 传输层

    定义传输数据的协议端口号,以及流控和差错校验。 协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层

  • 网络层 进行逻辑地址寻址,实现不同网络之间的路径选择。 协议有:IP(IPV4 IPV6)

  • 数据链路层

    建立逻辑连接、进行硬件地址寻址、差错校验 等功能。(由底层网络定义协议) 将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。

  • 物理层

    建立、维护、断开物理连接。(由底层网络定义协议)

:无明显分割单位,只保证前后的顺序正确

端口:接到的数据包分给各个应用

require('net'):node依赖库

IP地址

libnet/libpcap:libnet构造IP包并发送,libpcap从网卡抓所有流经网卡的包

全双工通道不存在优先关系,但是HTTP必须由客户端发起一个request,然后服务端返回response

二、服务端环境准备

  • POST / HTTP/1.1

    Request line

  • Host: 127.0.0.1
    Content-Type: application/x-www-form-urlencoded

    headers

  • field1=aaa&code=x%3D1

    body

const http = require('http');

http.createServer((request, response) => {
    let body = [];
    request.on('error',(err) => {
        console.error(err);
    }).on('data',(chunk) =>{
        body.push(chunk.toString())
    }).on('end',()=>{
        body = Buffer.concat(body).toString();
        console.log('body:',body);
        response.writeHead(200,{'Content-T':'text/html'});
        response.end("<div style='width:200px;height:200px;background:red'>123</div>")
    })
}).listen(8080);

console.log('server started');

三、实现一个HTTP请求

const net = require("net");

class Request {
    constructor(option) {
        this.method = option.method || 'GET';
        this.host = option.host;
        this.port = option.port || 80;
        this.path = option.path || '/';
        this.body = option.body || {};
        this.headers = option.headers || {};

        if(!this.headers["Content-Type"]){
            this.headers["Content-Type"] = "application/x-www-form-urlencoded"
        }

        if(this.headers["Content-Type"] ==="application/json"){
            this.bodyText = JSON.stringify(this.body)
        }else if(this.headers["Content-Type"] === "application/x-www-form-urlencoded"){
            this.bodyText = Object.keys(this.body).map(key => `${key}=${encodeURIComponent(this.body[key])}`).join('&')
        }

        this.headers["Content-Length"] = this.bodyText.length;
    }

    send(){
        return new  Promise((resolve, reject) => {
            //.....
        });
    }
}

void async function () {
    let request = new Request({
        method: "POST",
        host: "127.0.0.1",
        port: "8088",
        path: "/",
        headers: {
            ["X-Foo2"]: "customed"
        },
        body: {
            name: 'Midsummer'
        }
    })

    let response = await request.send();

    console.log(response);
}()

四、send函数编写,了解response格式

  • state line HTTP/1.1 200(HTTP状态码) OK(状态文本)
  • headers
  • body

五、发送请求

六、response解析

  • Response必须分段构造,所以我们要用一个ResponseParser来“装配”
  • ResponseParser分段处理ResponseText,我们用状态机来分析文本的结构

七、response body解析

  • Response的body可能根据Content-Type有不同的结构,因此我们会采用子Parser的结构来解决问题
  • 以TrunkedBodyParser为例,我们同样用状态机来处理body的格式