写一个http的request和response

 

新建文件夹Toy-Browser以及两个内部文件

//server.js
const http = require("http")

const server = http.createServer((req,res)=>{
    console.log('get client massage,zhangying')
    console.log(req.headers)
    //const ip = res.socket.remoteAddress;
  	//const port = res.socket.remotePort;
    
    res.setHeader('Content-Type','text/html');
    res.setHeader('X-Foo','bar');
    res.writeHead(200,{'Content-Type':'text/plain'});
    res.end('are you ok? yes,i am fine.');    
    // res.end(`Your IP address is ${ip} and your source port is ${port}.`);
    //res.end('ok');
})

server.listen(8088)
node server.js  //运行
//浏览器查看
http://127.0.0.1:8088/   or  http://localhost:8088/   
//浏览器中输入查看

var xhr = new XMLHttpRequest;
xhr.open("get","http:127.0.0.1:8088",true)
xhr.send(null);

xhr.responseText

xhr.HEADERS_RECEIVED  

 

//client.js 简版1

const net = require("net");

net.connect({
  host:"127.0.0.1",
  port: 8088,
  onread: {
    // Reuses a 4KiB Buffer for every read from the socket.
    buffer: Buffer.alloc(4 * 1024),
    callback: function(nread, buf) {
      // Received data is available in `buf` from 0 to `nread`.
      console.log(buf.toString('utf8', 0, nread));
    }
  }
});
//client.js 简版2
const net = require("net");

class Request{

}

class Response{

}

const client = net.createConnection({ 
    host:"127.0.0.1",
    port: 8088,
}, () => {
  // 'connect' listener.
  console.log('connected to server!');
  console.log('hello!');
    client.write(`POST / HTTP/1.1\r
Content-type: application/x-www-form-urlencoded\r
Content-length: 14\r
\r
name=zhangying`);
   // client.write(`POST / HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 14\r\n\r\nname=zhangying`);

});
client.on('data', (data) => {
  console.log(data.toString());
  client.end();
});
client.on('end', () => {
  console.log('disconnected from server');
});
client.on('error', (err) => {
    console.log(err);
    client.end()
});
//运行
node client.js

 

//client.js 完整版

const net = require("net");

class Request{
    //method url = host + +port + path
    //body k/r  
    //content-Type有四种方式
    constructor(options){
      this.method = options.method || "GET";
      this.host = options.host;
      this.port = options.port || '80';
      this.path = options.path || '/';
      this.body = options.body || {};
      this.headers = options.headers || {};
      // 设置默认的Content-Type
      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;
    }

    toString(){
      return `${this.method} ${this.path} HTTP/1.1\r
${Object.keys(this.headers).map(key => `${key}: ${this.headers[key]}`).join('\r\n')}
\r
${this.bodyText}`
    }

    send(connection) {
      return new Promise((resolve, reject)=>{
        const parser = new ResponseParser();

        if(connection){
          connection.write(this.toString());
        } else {
          connection = net.createConnection({
            host:this.host,
            port:this.port
          }, () =>{
            connection.write(this.toString())
          })
        }

        //不知道data是不是一个完整的Response
        connection.on('data', (data) => {
          parser.receive(data.toString())
          // console.log(parser.statusLine)
          // console.log(parser.headers)
          if(parser.isFinished){
            console.log(parser.Response)
            resolve(parser.Response)
          }
          // resolve(data.toString());
          connection.end();
        });

        connection.on('error', (err) => {
          reject(err);
          connection.end()
        });

      })

    
    }

}

class Response{

}

class ResponseParser{
  //状态机实现Buffer
  constructor(){
    this.WAITING_STATUS_LINE = 0;
    this.WAITING_STATUS_LINE_END = 1;  //status-line结束状态标识
    this.WAITING_HEADR_NAME = 2;
    this.WAITING_HEADR_SPACE = 3;
    this.WAITING_HEADR_VALUE = 4;
    this.WAITING_HEADR_LINE_END = 5;
    this.WAITING_HEADR_BLOCK_END = 6;
    this.WAITING_BODY = 7;

    this.current = this.WAITING_STATUS_LINE; //当前状态
    this.statusLine = "";
    this.headers = {};
    this.headerName = "";
    this.headerValue = "";
    this.bodyParser = null;

  }
  get isFinished(){
      return this.bodyParser && this.bodyParser.isFinished
  }

  get Response(){
      this.statusLine.match(/HTTP\/1.1 ([0-9]+) ([\s\S]+)/)
      return {
          statusCode:RegExp.$1,
          statusText:RegExp.$2,
          headers:this.headers,
          body:this.bodyParser.conetent.join('')
      }
  }

  receive(string){
    // console.log(string)
      for(let i = 0;i < string.length; i++){
          this.receiveChar(string.charAt(i))
      }
  }

  // 每次进来一个字符
  receiveChar(char){
    // console.log(JSON.stringify(char))
    
      if(this.current === this.WAITING_STATUS_LINE){

          if(char === "\r"){
              this.current = this.WAITING_STATUS_LINE_END
          }
          // 这一个判断要不要?
          else if(char === "\n"){
            this.current = this.WAITING_HEADR_NAME
          }
          else{
              this.statusLine += char
              // console.log(this.statusLine) // HTTP/1.1 200 OK
          }
      }

      else if(this.current === this.WAITING_STATUS_LINE_END){
          if(char === "\n"){
              this.current = this.WAITING_HEADR_NAME
          }
      }

      else if(this.current === this.WAITING_HEADR_NAME){
        //headName以冒号结束
          if(char === ":"){
              this.current = this.WAITING_HEADR_SPACE;
          }else if(char === '\r') {
            //WAITING_HEADR_BLOCK_END 要吃掉一个\n
            //header 结束了
            this.current = this.WAITING_HEADR_BLOCK_END;

            if(this.headers['Transfer-Encoding'] === 'chunked'){
              // header结束 body开始
              this.bodyParser = new TrunkedBodyParser();
            }

          }else {
            this.headerName += char;
            // console.log(this.headerName)
          }
      }

      else if(this.current === this.WAITING_HEADR_SPACE){
          if(char === " "){
              this.current = this.WAITING_HEADR_VALUE;
          }
      }

      else if(this.current === this.WAITING_HEADR_VALUE){
          if(char === "\r"){
              this.current = this.WAITING_HEADR_LINE_END;
              //head是有多行的
              this.headers[this.headerName] =  this.headerValue
              this.headerName = "";
              this.headerValue = "";
          }else{
              this.headerValue += char;
              // console.log(this.headerValue)
          }
      }

      else if(this.current === this.WAITING_HEADR_LINE_END){
          if(char === "\n"){
            //循环  进入到header的下一行
            this.current = this.WAITING_HEADR_NAME;
          }
      }

      else if(this.current === this.WAITING_HEADR_BLOCK_END){
        if(char === "\n"){
            this.current = this.WAITING_BODY;
        }
      }

      else if(this.current = this.WAITING_BODY){
        // console.log(this.bodyParser)
        this.bodyParser.receiveChar(char);
      }
  }
}

class TrunkedBodyParser {
  constructor(){
      this.WAITING_LENGTH = 0
      this.WAITING_LENGTH_LINE_END = 1;
      this.READING_TRUNK = 2;
      this.WAITING_NEW_LINE = 3;
      this.WAITING_NEW_LINE_END = 4;

      // 表示剩下的字符长度
      this.length = 0;
      this.conetent = []
      this.isFinished = false;
      this.current = this.WAITING_LENGTH

  }

  receiveChar(char){
      // console.log(JSON.stringify(char))
      console.log(this.current)
      if(this.current === this.WAITING_LENGTH){
          if(char === '\r') {
              
            // 结束
              if( this.length === 0){
                  console.log('//isFinished//')
                  this.isFinished = true
              }
              // 获取到了长度,进入下一个状态
              this.current = this.WAITING_LENGTH_LINE_END;
          }else {
              //1a 十六进制
              this.length *= 16;
              if(char.charCodeAt(0) > 96 && char.charCodeAt(0) < 103){
                this.length += char.charCodeAt(0) - 'a'.charCodeAt(0) + 10;
              } else if(char.charCodeAt(0) > 64 && char.charCodeAt(0) < 71){
                this.length += char.charCodeAt(0) - 'A'.charCodeAt(0) + 10;
              }else{
                this.length += char.charCodeAt(0) - '0'.charCodeAt(0);
              }
          }
      }

      else  if(this.current === this.WAITING_LENGTH_LINE_END){
          if(char === '\n') {
              this.current = this.READING_TRUNK;
          }
      }

      
      else  if(this.current === this.READING_TRUNK){
              if(this.length > 0){
                this.conetent.push(char)
                this.length --;
              }
              if(this.length === 0) {
                  // console.log(this.conetent)
                  this.current = this.WAITING_NEW_LINE
              }
      }

      else  if(this.current === this.WAITING_NEW_LINE){
          if(char === '\r') {
              this.current = this.WAITING_NEW_LINE_END
          }
      }

      else  if(this.current = this.WAITING_NEW_LINE_END){
        if(char === '\n') {
          // 循环
            this.current = this.WAITING_LENGTH
        }
      }
  }
}

void async function(){

  let request = new Request({
    method:"POST",
    host:'127.0.0.1',
    port:'8088',
    path:"/",
    headers:{
      ["X-Foo2"]:"customed"
    },
    body:{
      name:"zhangying"
    }
  })
  // console.log(request.toString())
  let response = await request.send()
  // console.log(response)
}();

参考: https://nodejs.org/dist/latest-v12

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值