偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

聊聊No.js 支持 HTTP 模塊

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

[[427158]]

1 HTTP 解析器

No.js 使用 Node.js 的 HTTP 解析器 llhttp 實現(xiàn) HTTP 協(xié)議的解析,llhttp 負(fù)責(zé)解析 HTTP 報文,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 是對 llhttp 的封裝,主要是注冊 llhttp 的鉤子,llhttp 在解析 HTTP 報文的時候會回調(diào) HTTP_Parser 的鉤子。比較麻煩的是需要在 HTTP_Parser 對象里保存 llhttp 的解析結(jié)果,把 HTTP_Parser 類的成員函數(shù)轉(zhuǎn)成 c 函數(shù)作為 llhttp 的回調(diào)非常麻煩,問題在于如何在 llhttp 執(zhí)行回調(diào)的時候找到對應(yīng)的 HTTP_Parser 對象。比如 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 對象,最終發(fā)現(xiàn) llhttp 提供了 data 字段關(guān)聯(lián)上下文。所以在 HTTP_Parser 初始化時關(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; 

我們在 llhttp 回調(diào)時通過 data 字段就可以取得 HTTP_Parser 對象。下面是所有鉤子的實現(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 的封裝后,接著需要把這個能力暴露到 JS 層??匆幌?C++ 模塊到定義。

  1. class Parser : public BaseObject { 
  2.   public
  3.       Parser(Environment* env, Local<Object> object): BaseObject(env, object) { 
  4.           // 注冊到 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++ 模塊到定義非常簡單,只是對 HTTP_Parser 的封裝,然后通過 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 層,并提供一個 parse方法。JS 層拿到 TCP 層的數(shù)據(jù)后,通過執(zhí)行 parse 進(jìn)行 HTTP 協(xié)議的解析,我們看看 parse 對應(yīng)函數(shù) No::HTTP::Parser::Parse 的實現(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首先通過 args 拿到 C++ 的對象 Parser(熟悉 Node.js 的同學(xué)應(yīng)該很容易明白這個處理方式)。接著調(diào)用 HTTP_Parser 的 parse 方法,在解析的過程中,llhttp 就會執(zhí)行 HTTP_Parser 的回調(diào), HTTP_Parser 就會執(zhí)行 Parser 對象的回調(diào),Parser 就會執(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)出,接下來就是 JS 層的實現(xiàn),首先看看 一個使用例子。

  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 很相似,接下來看看具體實現(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 的時候會監(jiān)聽傳入的地址,從而啟動一個服務(wù)器,listen 回調(diào)執(zhí)行說明有連接到來,我們新建一個 ServerSocket 對象表示和客戶端通信的 Socket。并觸發(fā) connection 事件到上層。接著看 ServerSocket 的實現(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 的實現(xiàn)目前很簡單,主要是讀取數(shù)據(jù)并觸發(fā) data 事件,因為 TCP 只是負(fù)責(zé)數(shù)據(jù)傳輸,不負(fù)責(zé)數(shù)據(jù)解析。有了這個能力后,我們看看 http 層的實現(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 的時候,會先執(zhí)行 tcp 模塊啟動一個服務(wù)器,http 層監(jiān)聽 connection 事件等待連接到來,有連接到來時,http 創(chuàng)建一個 HTTPRequest 對象表示 http 請求。

  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. 新建一個 HTTPParser 解析 HTTP 協(xié)議。3. 監(jiān)聽 data 事件,收到 TCP 層數(shù)據(jù)后調(diào)用 HTTP 解析器解析。4. 注冊 HTTP 解析的回調(diào)鉤子,就是前面講到的。等到解析完 HTTP header 后,也就是執(zhí)行 onHeaderComplete 回調(diào)后,No.js 就會通過觸發(fā) request事件 回調(diào)業(yè)務(wù)層,也就是 createServer 傳入的回調(diào)。業(yè)務(wù)層可以監(jiān)聽 HTTPRequest 的 data 事件,當(dāng) HTTP 請求有 body 數(shù)據(jù)時,就會注冊 HTTPRequest 的 data 事件回調(diào)業(yè)務(wù)層。

4 總結(jié)

 

 

 

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

 

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

2021-07-09 00:24:10

No.jsNode.js原理

2021-09-16 05:32:31

No.js 模塊加載器module1.js

2021-09-26 05:06:04

Node.js模塊機制

2023-03-27 08:49:51

2024-02-29 18:06:39

HTTP性能優(yōu)化

2023-06-30 23:25:46

HTTP模塊內(nèi)存

2021-10-03 15:02:50

HTTPNodejs

2022-05-09 08:34:01

FeignhttpJava

2021-10-05 20:12:57

No.jsV8 編碼

2022-09-30 00:03:03

JS斷點線程

2022-10-08 00:07:00

JSV8調(diào)用棧

2021-09-05 17:46:21

云計算No.jsio_uringJS

2016-11-28 09:00:10

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

2020-10-22 10:43:55

HTTP框架AOP

2022-05-27 07:01:48

JSGIF總幀數(shù)

2021-01-27 05:28:38

工具RestSharpHTTP

2022-06-13 07:36:47

useEffectHooks

2021-11-06 18:40:27

js底層模塊

2024-05-13 08:04:26

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

2024-04-07 08:23:01

JS隔離JavaScript
點贊
收藏

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