日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
聊聊No.js支持HTTP模塊

[[427158]]

1 HTTP 解析器

No.js 使用 Node.js 的 HTTP 解析器 llhttp 實現(xiàn) HTTP 協(xié)議的解析,llhttp 負責解析 HTTP 報文,No.js 需要做的事情是保存解析的結果并封裝具體的能力??纯?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 char* at, size_t length); 
  13.         int on_url(llhttp_t* parser, const char* at, size_t length); 
  14.         int on_header_field(llhttp_t* parser, const char* at, size_t length); 
  15.         int on_header_value(llhttp_t* parser, const char* at, size_t length); 
  16.         int on_headers_complete(llhttp_t* parser); 
  17.         int on_body(llhttp_t* parser, const char* at, 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 keys; 
  32.         vector 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 報文的時候會回調 HTTP_Parser 的鉤子。比較麻煩的是需要在 HTTP_Parser 對象里保存 llhttp 的解析結果,把 HTTP_Parser 類的成員函數(shù)轉成 c 函數(shù)作為 llhttp 的回調非常麻煩,問題在于如何在 llhttp 執(zhí)行回調的時候找到對應的 HTTP_Parser 對象。比如 llhttp 的 on_message_begin 回調格式是

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

我們看到回調里只有 llhttp 相關的數(shù)據(jù)結構,拿不到 HTTP_Parser 對象,最終發(fā)現(xiàn) llhttp 提供了 data 字段關聯(lián)上下文。所以在 HTTP_Parser 初始化時關聯(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 回調時通過 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 的關聯(lián)。解析完 HTTP 協(xié)議后,最終還需要回調 No.js 的 JS 層。HTTP_Parser 目前支持三種回調。

 
 
 
 
  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): BaseObject(env, object) { 
  4.           // 注冊到 HTTP_Parser 的回調 
  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 cb; 
  12.                   Local context = parser->env()->GetContext(); 
  13.                   Isolate * isolate = parser->env()->GetIsolate(); 
  14.                   Local  key = newStringToLcal(isolate, "onBodyComplete"); 
  15.                   parser->object()->Get(context, key).ToLocal(&cb); 
  16.                   // 回調 JS 層 
  17.                   if (!cb.IsEmpty() && cb->IsFunction()) { 
  18.                       Local argv[] = { 
  19.                           newStringToLcal(isolate, info.body.c_str()) 
  20.                       }; 
  21.                       cb.As()->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& args);  
  29.       static void New(const FunctionCallbackInfo& args);  
  30.   private: 
  31.       HTTP_Parser * httpparser; 
  32. }; 
  33. C++ 模塊到定義非常簡單,只是對 HTTP_Parser 的封裝,然后通過 V8 導出能力到 JS 層。

     
     
     
     
    1. void No::HTTP::Init(Isolate* isolate, Local target) { 
    2.     Local 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()); 
    7. 我們看到 C++ 模塊導出了 HTTPParser 到 JS 層,并提供一個 parse方法。JS 層拿到 TCP 層的數(shù)據(jù)后,通過執(zhí)行 parse 進行 HTTP 協(xié)議的解析,我們看看 parse 對應函數(shù) No::HTTP::Parser::Parse 的實現(xiàn)。

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

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

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

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

      3 JS 層

      完成了底層的封裝和能力導出,接下來就是 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)聽傳入的地址,從而啟動一個服務器,listen 回調執(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 只是負責數(shù)據(jù)傳輸,不負責數(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 模塊,所以我們調用 http.createServer 的時候,會先執(zhí)行 tcp 模塊啟動一個服務器,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ù)后調用 HTTP 解析器解析。4. 注冊 HTTP 解析的回調鉤子,就是前面講到的。等到解析完 HTTP header 后,也就是執(zhí)行 onHeaderComplete 回調后,No.js 就會通過觸發(fā) request事件 回調業(yè)務層,也就是 createServer 傳入的回調。業(yè)務層可以監(jiān)聽 HTTPRequest 的 data 事件,當 HTTP 請求有 body 數(shù)據(jù)時,就會注冊 HTTPRequest 的 data 事件回調業(yè)務層。

      4 總結

       

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


      分享文章:聊聊No.js支持HTTP模塊
      當前網(wǎng)址:http://m.5511xx.com/article/dpjeidc.html