自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊No.js 支持 HTTP 模塊

開(kāi)發(fā) 前端
No.js 初步支持了 HTTP 能力,目前只是支持解析 HTTP 請(qǐng)求,很多地方還需要慢慢琢磨,本文簡(jiǎn)單介紹其實(shí)現(xiàn)。

[[427158]]

1 HTTP 解析器

No.js 使用 Node.js 的 HTTP 解析器 llhttp 實(shí)現(xiàn) HTTP 協(xié)議的解析,llhttp 負(fù)責(zé)解析 HTTP 報(bào)文,No.js 需要做的事情是保存解析的結(jié)果并封裝具體的能力??纯?No.js 是如何封裝 llhttp 的。

  1. class HTTP_Parser { 
  2.     public
  3.         HTTP_Parser(llhttp_type type, parser_callback callbacks = {}) { 
  4.             llhttp_init(&parser, type, &HTTP_Parser::settings); 
  5.             // set data after llhttp_init, because llhttp_init will call memset to fill zero to memory  
  6.             parser.data = this; 
  7.             memset((void *)&callback, 0, sizeof(callback)); 
  8.             callback = callbacks; 
  9.         } 
  10.  
  11.         int on_message_begin(llhttp_t* parser); 
  12.         int on_status(llhttp_t* parser, const charat, size_t length); 
  13.         int on_url(llhttp_t* parser, const charat, size_t length); 
  14.         int on_header_field(llhttp_t* parser, const charat, size_t length); 
  15.         int on_header_value(llhttp_t* parser, const charat, size_t length); 
  16.         int on_headers_complete(llhttp_t* parser); 
  17.         int on_body(llhttp_t* parser, const charat, size_t length); 
  18.         int on_message_complete(llhttp_t* parser); 
  19.         int parse(const char* data, int len); 
  20.         void print(); 
  21.     private:  
  22.         unsigned char major_version; 
  23.         unsigned char minor_version; 
  24.         unsigned char upgrade; 
  25.         unsigned char keepalive; 
  26.         time_t parse_start_time; 
  27.         time_t header_end_time; 
  28.         time_t message_end_time; 
  29.         string url; 
  30.         string status; 
  31.         vector<string> keys; 
  32.         vector<string> values
  33.         string body; 
  34.         llhttp_t parser; 
  35.         parser_callback callback; 
  36.         static llhttp_settings_t settings; 
  37. }; 

HTTP_Parser 是對(duì) llhttp 的封裝,主要是注冊(cè) llhttp 的鉤子,llhttp 在解析 HTTP 報(bào)文的時(shí)候會(huì)回調(diào) HTTP_Parser 的鉤子。比較麻煩的是需要在 HTTP_Parser 對(duì)象里保存 llhttp 的解析結(jié)果,把 HTTP_Parser 類的成員函數(shù)轉(zhuǎn)成 c 函數(shù)作為 llhttp 的回調(diào)非常麻煩,問(wèn)題在于如何在 llhttp 執(zhí)行回調(diào)的時(shí)候找到對(duì)應(yīng)的 HTTP_Parser 對(duì)象。比如 llhttp 的 on_message_begin 回調(diào)格式是

  1. typedef int (*llhttp_cb)(llhttp_t*); 

我們看到回調(diào)里只有 llhttp 相關(guān)的數(shù)據(jù)結(jié)構(gòu),拿不到 HTTP_Parser 對(duì)象,最終發(fā)現(xiàn) llhttp 提供了 data 字段關(guān)聯(lián)上下文。所以在 HTTP_Parser 初始化時(shí)關(guān)聯(lián) llhttp 和 HTTP_Parser 的上下文。

  1. HTTP_Parser(llhttp_type type, parser_callback callbacks = {}) { 
  2.     llhttp_init(&parser, type, &HTTP_Parser::settings); 
  3.     parser.data = this; 

我們?cè)?llhttp 回調(diào)時(shí)通過(guò) data 字段就可以取得 HTTP_Parser 對(duì)象。下面是所有鉤子的實(shí)現(xiàn)。

  1. llhttp_settings_t No::HTTP::HTTP_Parser::settings = { 
  2.     [](llhttp_t * parser) { 
  3.         return ((HTTP_Parser *)parser->data)->on_message_begin(parser); 
  4.     }, 
  5.     [](llhttp_t * parser, const char * data, size_t len) { 
  6.         return ((HTTP_Parser *)parser->data)->on_url(parser, data, len); 
  7.     }, 
  8.     [](llhttp_t * parser, const char * data, size_t len) { 
  9.         return ((HTTP_Parser *)parser->data)->on_status(parser, data, len); 
  10.     }, 
  11.     [](llhttp_t * parser, const char * data, size_t len) { 
  12.         return ((HTTP_Parser *)parser->data)->on_header_field(parser, data, len); 
  13.     }, 
  14.     [](llhttp_t * parser, const char * data, size_t len) { 
  15.         return ((HTTP_Parser *)parser->data)->on_header_value(parser, data, len); 
  16.     }, 
  17.     [](llhttp_t * parser) { 
  18.         return ((HTTP_Parser *)parser->data)->on_headers_complete(parser); 
  19.     }, 
  20.     [](llhttp_t * parser, const char * data, size_t len) { 
  21.         return ((HTTP_Parser *)parser->data)->on_body(parser, data, len); 
  22.     }, 
  23.     [](llhttp_t * parser) { 
  24.         return ((HTTP_Parser *)parser->data)->on_message_complete(parser); 
  25.     } 
  26. }; 

這樣就完成了 llhttp 和 No.js 的關(guān)聯(lián)。解析完 HTTP 協(xié)議后,最終還需要回調(diào) No.js 的 JS 層。HTTP_Parser 目前支持三種回調(diào)。

  1. struct parser_callback { 
  2.     void * data; 
  3.     p_on_headers_complete on_headers_complete; 
  4.     p_on_body on_body; 
  5.     p_on_body_complete on_body_complete; 
  6. }; 

2 HTTP C++ 模塊

完成了 llhttp 的封裝后,接著需要把這個(gè)能力暴露到 JS 層??匆幌?C++ 模塊到定義。

  1. class Parser : public BaseObject { 
  2.   public
  3.       Parser(Environment* env, Local<Object> object): BaseObject(env, object) { 
  4.           // 注冊(cè)到 HTTP_Parser 的回調(diào) 
  5.           parser_callback callback = { 
  6.               this, 
  7.               ..., 
  8.               ..., 
  9.               [](on_body_complete_info info, parser_callback callback) { 
  10.                   Parser * parser = (Parser *)callback.data; 
  11.                   Local<Value> cb; 
  12.                   Local<Context> context = parser->env()->GetContext(); 
  13.                   Isolate * isolate = parser->env()->GetIsolate(); 
  14.                   Local <String> key = newStringToLcal(isolate, "onBodyComplete"); 
  15.                   parser->object()->Get(context, key).ToLocal(&cb); 
  16.                   // 回調(diào) JS 層 
  17.                   if (!cb.IsEmpty() && cb->IsFunction()) { 
  18.                       Local<Value> argv[] = { 
  19.                           newStringToLcal(isolate, info.body.c_str()) 
  20.                       }; 
  21.                       cb.As<v8::Function>()->Call(context, parser->object(), 1, argv);   
  22.                   } 
  23.               }, 
  24.           }; 
  25.           httpparser = new HTTP_Parser(HTTP_REQUEST, callback); 
  26.       } 
  27.       void Parse(const char * data, size_t len);  
  28.       static void Parse(const FunctionCallbackInfo<Value>& args);  
  29.       static void New(const FunctionCallbackInfo<Value>& args);  
  30.   private: 
  31.       HTTP_Parser * httpparser; 
  32. }; 

C++ 模塊到定義非常簡(jiǎn)單,只是對(duì) HTTP_Parser 的封裝,然后通過(guò) V8 導(dǎo)出能力到 JS 層。

  1. void No::HTTP::Init(Isolate* isolate, Local<Object> target) { 
  2.     Local<FunctionTemplate> parser = FunctionTemplate::New(isolate, No::HTTP::Parser::New); 
  3.     parser->InstanceTemplate()->SetInternalFieldCount(1); 
  4.     parser->SetClassName(newStringToLcal(isolate, "HTTPParser")); 
  5.     parser->PrototypeTemplate()->Set(newStringToLcal(isolate, "parse"), FunctionTemplate::New(isolate, No::HTTP::Parser::Parse)); 
  6.     setObjectValue(isolate, target, "HTTPParser", parser->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()); 

我們看到 C++ 模塊導(dǎo)出了 HTTPParser 到 JS 層,并提供一個(gè) parse方法。JS 層拿到 TCP 層的數(shù)據(jù)后,通過(guò)執(zhí)行 parse 進(jìn)行 HTTP 協(xié)議的解析,我們看看 parse 對(duì)應(yīng)函數(shù) No::HTTP::Parser::Parse 的實(shí)現(xiàn)。

  1. void No::HTTP::Parser::Parse(const FunctionCallbackInfo<Value>& args) { 
  2.     Parser * parser = (Parser *)unwrap(args.This()); 
  3.     Local<ArrayBuffer> arrayBuffer = args[0].As<ArrayBuffer>(); 
  4.     std::shared_ptr<BackingStore> backing = arrayBuffer->GetBackingStore(); 
  5.     const char * data = (const char * )backing->Data(); 
  6.     parser->Parse(data, strlen(data)); 

Parse首先通過(guò) args 拿到 C++ 的對(duì)象 Parser(熟悉 Node.js 的同學(xué)應(yīng)該很容易明白這個(gè)處理方式)。接著調(diào)用 HTTP_Parser 的 parse 方法,在解析的過(guò)程中,llhttp 就會(huì)執(zhí)行 HTTP_Parser 的回調(diào), HTTP_Parser 就會(huì)執(zhí)行 Parser 對(duì)象的回調(diào),Parser 就會(huì)執(zhí)行 JS 回調(diào)。比如解析完 body 后執(zhí)行 JS 層回調(diào)。

  1. [](on_body_complete_info info, parser_callback callback) { 
  2.     Parser * parser = (Parser *)callback.data; 
  3.     Local<Value> cb; 
  4.     Local<Context> context = parser->env()->GetContext(); 
  5.     Isolate * isolate = parser->env()->GetIsolate(); 
  6.     Local <String> key = newStringToLcal(isolate, "onBodyComplete"); 
  7.     parser->object()->Get(context, key).ToLocal(&cb); 
  8.     if (!cb.IsEmpty() && cb->IsFunction()) { 
  9.         Local<Value> argv[] = { 
  10.             newStringToLcal(isolate, info.body.c_str()) 
  11.         }; 
  12.         cb.As<v8::Function>()->Call(context, parser->object(), 1, argv);   
  13.     } 
  14. }, 

就是找到 JS 設(shè)置的 onBodyComplete 函數(shù)并執(zhí)行。結(jié)構(gòu)如下。

3 JS 層

完成了底層的封裝和能力導(dǎo)出,接下來(lái)就是 JS 層的實(shí)現(xiàn),首先看看 一個(gè)使用例子。

  1. const { 
  2.     console,} = No;const { http } = No.libs; 
  3.  
  4. http.createServer({host: '127.0.0.1', port: 8888}, (req, res) => { 
  5.     console.log(JSON.stringify(req.headers)); 
  6.     req.on('data', (buffer) => { 
  7.         console.log(buffer); 
  8.     }); 
  9. }); 

和 Node.js 很相似,接下來(lái)看看具體實(shí)現(xiàn)。先看 TCP 層的封裝。

  1. class Server extends events { 
  2.     fd = -1; 
  3.     connections = 0; 
  4.     constructor(options = {}) { 
  5.         super(); 
  6.         const fd = tcp.socket(constant.domain.AF_INET, constant.type.SOCK_STREAM); 
  7.         this.fd = fd; 
  8.         tcp.bind(fd, options.host, options.port); 
  9.         tcp.listen(fd, 512, (clientFd) => { 
  10.             this.connections++; 
  11.             const serverSocket = new ServerSocket({fd: clientFd}); 
  12.             this.emit('connection', serverSocket); 
  13.         }); 
  14.     } 

createServer 的時(shí)候會(huì)監(jiān)聽(tīng)傳入的地址,從而啟動(dòng)一個(gè)服務(wù)器,listen 回調(diào)執(zhí)行說(shuō)明有連接到來(lái),我們新建一個(gè) ServerSocket 對(duì)象表示和客戶端通信的 Socket。并觸發(fā) connection 事件到上層。接著看 ServerSocket 的實(shí)現(xiàn)

  1. class ServerSocket extends Socket { 
  2.     constructor(options = {}) { 
  3.         super(options); 
  4.         this.fd = options.fd; 
  5.         this.read(); 
  6.     } 
  7.     read() { 
  8.         const buffer = new ArrayBuffer(1024); 
  9.         tcp.read(this.fd, buffer, 0, (status) => { 
  10.             this.emit('data', buffer); 
  11.             this.read(); 
  12.         }) 
  13.     } 

ServerSocket 的實(shí)現(xiàn)目前很簡(jiǎn)單,主要是讀取數(shù)據(jù)并觸發(fā) data 事件,因?yàn)?TCP 只是負(fù)責(zé)數(shù)據(jù)傳輸,不負(fù)責(zé)數(shù)據(jù)解析。有了這個(gè)能力后,我們看看 http 層的實(shí)現(xiàn)。

  1. function createServer(...arg) { 
  2.     return new Server(...arg);} 
  3.  
  4. class Server extends No.libs.tcp.Server { 
  5.     constructor(options = {}, cb) { 
  6.         super(options); 
  7.         this.options = options; 
  8.         if (typeof cb === 'function') { 
  9.             this.on('request', cb); 
  10.         } 
  11.         this.on('connection', (socket) => { 
  12.             new HTTPRequest({socket, server: this}); 
  13.         }); 
  14.     } 

http 模塊繼承于 tcp 模塊,所以我們調(diào)用 http.createServer 的時(shí)候,會(huì)先執(zhí)行 tcp 模塊啟動(dòng)一個(gè)服務(wù)器,http 層監(jiān)聽(tīng) connection 事件等待連接到來(lái),有連接到來(lái)時(shí),http 創(chuàng)建一個(gè) HTTPRequest 對(duì)象表示 http 請(qǐng)求。

  1. class HTTPRequest extends No.libs.events { 
  2.     socket = null
  3.     httpparser = null
  4.     constructor({socket, server}) { 
  5.         super(); 
  6.         this.server = server; 
  7.         this.socket = socket; 
  8.         this.httpparser = new HTTPParser(); 
  9.         this.httpparser.onHeaderComplete = (data) => { 
  10.             this.major = data.major; 
  11.             this.minor = data.minor; 
  12.             this.keepalive = data.keepalive; 
  13.             this.upgrade = data.upgrade; 
  14.             this.headers = data.headers; 
  15.             this.server.emit('request', this); 
  16.         } 
  17.         this.httpparser.onBody = (data) => { 
  18.             this.emit('data', data); 
  19.         } 
  20.         this.httpparser.onBodyComplete = (data) => { 
  21.             // console.log(data); 
  22.         } 
  23.  
  24.         socket.on('data', (buffer) => { 
  25.             this.httpparser.parse(buffer); 
  26.         }); 
  27.     } 

HTTPRequest 的邏輯如下 1. 保存底層的 socket 2. 新建一個(gè) HTTPParser 解析 HTTP 協(xié)議。3. 監(jiān)聽(tīng) data 事件,收到 TCP 層數(shù)據(jù)后調(diào)用 HTTP 解析器解析。4. 注冊(cè) HTTP 解析的回調(diào)鉤子,就是前面講到的。等到解析完 HTTP header 后,也就是執(zhí)行 onHeaderComplete 回調(diào)后,No.js 就會(huì)通過(guò)觸發(fā) request事件 回調(diào)業(yè)務(wù)層,也就是 createServer 傳入的回調(diào)。業(yè)務(wù)層可以監(jiān)聽(tīng) HTTPRequest 的 data 事件,當(dāng) HTTP 請(qǐng)求有 body 數(shù)據(jù)時(shí),就會(huì)注冊(cè) HTTPRequest 的 data 事件回調(diào)業(yè)務(wù)層。

4 總結(jié)

 

 

 

雖然目前只是粗糙地實(shí)現(xiàn)了 HTTP 模塊,但實(shí)現(xiàn)的過(guò)程中,涉及到的內(nèi)容還是挺多的,后面有時(shí)間再慢慢完善。有興趣的同學(xué)可以到 https://github.com/theanarkh/No.js 了解。

 

責(zé)任編輯:武曉燕 來(lái)源: 編程雜技
相關(guān)推薦

2021-09-16 05:32:31

No.js 模塊加載器module1.js

2021-07-09 00:24:10

No.jsNode.js原理

2021-09-26 05:06:04

Node.js模塊機(jī)制

2024-02-29 18:06:39

HTTP性能優(yōu)化

2023-03-27 08:49:51

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2022-05-09 08:34:01

FeignhttpJava

2021-10-03 15:02:50

HTTPNodejs

2022-09-30 00:03:03

JS斷點(diǎn)線程

2022-10-08 00:07:00

JSV8調(diào)用棧

2021-10-05 20:12:57

No.jsV8 編碼

2020-10-22 10:43:55

HTTP框架AOP

2016-11-28 09:00:10

瀏覽器瀏覽器緩存服務(wù)端

2021-09-05 17:46:21

云計(jì)算No.jsio_uringJS

2022-05-27 07:01:48

JSGIF總幀數(shù)

2021-01-27 05:28:38

工具RestSharpHTTP

2024-05-13 08:04:26

Vue.jsWeb應(yīng)用程序

2024-04-07 08:23:01

JS隔離JavaScript

2021-11-06 18:40:27

js底層模塊

2022-04-27 09:28:11

HTTPExpires
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)