日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

Mochiweb的設計分析

系統 2094 0

?

Mochiweb的設計分析

March 15th, 2009 :: refactor

轉自: http://erlang-china.org/misc/mochiweb-inside.html

Web服務器 的基本工作大致分3步:?

  1. 接收HTTP請求;?
  2. 處理HTTP請求,生成響應內容;
  3. 發送響應

一、處理請求和發送響應 ?

模塊mochiweb_request可說是Mochiweb處理HTTP請求的核心部分,它總共負責了第2步和第3步工作。因此參數化模塊mochiweb_request的實例不像它的模塊名那樣單純:它還負責將請求的響應(通過Socket連接,調用gen_tcp:send)發還(response)給瀏覽器。這個模塊是一個 參數化模塊 。這意味著它具有基于對象(Object-based)的特點,它的每個實例化對象代表一個用戶請求,用戶可以像OO那樣操作HTTP請求:獲取請求信息,生成響應內容等。?

對靜態內容(如html文件,靜態圖片)來說,無非是讀取文件系統中靜態文件的內容作為響應消息體(Body)發送給客戶端瀏覽器。?

對動態內容來說,生成響應內容的業務邏輯是用戶通過編程實現的:解析請求(包括URL路徑和HTTP請求頭),然后生成相應的響應內容。用戶編寫業務邏輯處理函數,然后將函數插入到mochiweb中。此函數帶有一參數,該參數是參數化模塊mochiweb_request的實例(假設為Req),在這個定制函數中可以通過這個Req實例對象獲取瀏覽器請求的所有信息(包括URL路徑和HTTP請求頭),處理后的響應數據也通過Req實例提供的函數發還給瀏覽器,有好幾種方式:?

  1. 通過Req實例對象直接發還給瀏覽器

    ?

      a)? 通過這個Req實例對象的response/1函數直接發還給瀏覽器:
      Req:response({Code, ResponseHeaders, Body})
      這個函數接收一個tuple參數,參數的第一個元素是HTTP狀態碼(比如200),第二個元素是響應消息頭列表(一個二元tuple,key和value都是字符串,如{“Content-Type”, “image/jpeg”}),第三個元素即為HTTP響應內容;如果是動態內容,采用response函數直接發還響應數據還是比較方便的。

      b)? 如果是靜態文件,通過Req的serve_file(…)函數可以直接將文件發還給瀏覽器,告訴此函數文件的Web根目錄(doc_root)和相對路徑就可以將指定的靜態文件發給瀏覽器了。doc_root目錄一般在配置文件中設置,這個目錄下的所有文件都可以通過HTTP訪問。

      c)? 一些常見的例行響應:
      不存在的URL請求返回404錯誤,可以直接調用Req:not_found() 若一切正常,調用Req:ok({…, Body})直接返回狀態碼為200的HTTP響應
      b,c兩種情況的內部其實還是調用的Req:response()

  2. 通過Response模塊對象將響應發還給瀏覽器

    ?

      Req:response函數會返回一個mochiweb_response參數化模塊實例對象(假設為Response),Response實例對象包含有對應的Req實例對象。通過Response對象可以得到響應的相關信息(如響應狀態碼,響應消息頭,對應的Req對象),它還有一個send函數可以將響應數據發還給瀏覽器(它的實現其實還是調用Req對象的send函數進行的)。Response之所以還要有send函數是為了發送chunked數據(HTTP 1.1)的方便,在第一次響應完成后,后繼的chunk數據就可以通過最初返回的Response對象繼續進行發送了,為此Response有個函數write_chunk()專門干這事,write_chunk檢查了請求消息頭中的HTTP 版本消息后就調用Response:send。

      因此,響應內容最終都是由參數化模塊mochiweb_request的response/1函數發送的。而這個response(…)函數內部最后調用了Req:send(Data)函數將響應通過socket連接(調用gen_tcp:send)返還給瀏覽器,這一發送過程又分成兩個階段:響應消息頭(Headers)的發送和消息體(Body)的發送,這兩步都是通過Req:send完成的。

小結:對于程序員來說,他編寫的服務器端處理HTTP請求的函數必須帶有一個參數,這個參數代表了mochiweb_request參數化模塊的一個實例對象。通過這個實例程序員可以得到HTTP請求的所有信息(包括路徑、請求參數以及HTTP請求消息頭),然后生成響應,他還可以通過該mochiweb_request實例對象將響應數據發還給瀏覽器。?

  • 如果響應的是靜態文件,可以通過Request:server_file()函數發送響應;
  • 如果響應是動態生成的內容,通過Request:response()函數發送響應數據(文本數據和二進制數據都可以);
  • 如果是chunk響應,第一次調用Request:response()函數發送數據后,該函數會返回一個Response實例對象(參數化模塊mochiweb_response的一個實例),以后的響應數據可以通過這個Response實例對象(調用Response:write_chunk(Data))發送后續的響應數據。

二、Web服務器的業務處理邏輯如何嵌入到Mochiweb ?

業務處理邏輯被程序員編寫成一個函數(函數式編程中函數也是一種數據,以下稱為HttpLoop),處理函數HttpLoop是作為mochiweb啟動時的參數之一進入Mochiweb,Mochiweb是這樣啟動的:?

mochiweb_http : start ([ { loop ,? HttpLoop },... ]) ??

Request實例對象最終要調用gen_tcp:send(Socket,…)將響應數據由socket連接發給瀏覽器,因此,Request實例對象應該持有HTTP請求的socket連接。這個目標是這樣實現的:?
在啟動過程中,mochiweb_http又對用戶的HttpLoop函數進行了重新包裝?

Loop ?=? fun ? ( Socket ) ?->
?? ?
mochiweb_http : loop ( Socket ,? HttpLoop )
end ,

每個瀏覽器連接請求對應著一個Socket連接,新的Loop函數以此Socket作為參數,然后通過mochiweb_http:loop函數對Socket連接和用戶自定義的HttpLoop函數進行了處理,簡單的說,這個函數做了如下工作:?

  1. 將Socket連接設置成能處理HTTP數據包 (inet:setopts(Socket, [{packet, http}]));
  2. 準備接收socket連接傳來的數據(gen_tcp:recv(Socket,…));

當收到數據時:

  1. 將HTTP消息頭數據提出(成為{HeaderName, HeaderValue}的tuple列表)
  2. 生成一個參數化模塊mochiweb_request的實例對象,并將Socket連接、HTTP請求信息(路徑、請求方法、HTTP版本)以及請求消息頭列表包裝到此實例對象中,
  3. 然后調用用戶的HttpLoop對請求進行處理和響應(如前所述,處理得到的響應數據也在用戶編寫的HttpLoop函數中被發送給瀏覽器)
  4. 最后根據HTTP請求信息決定是簡單的關閉Socket連接,還是清理一下Req對象并保持連接(例如對keep-alive,chunk等類型的HTTP請求,以及還沒完成數據傳送的HTTP請求),以便繼續讓HttpLoop函數進行處理,

這都是在headers函數中進行的,還是看mochiweb_http模塊的代碼:?

  1. headers ( Socket ,? Request ,? Headers ,? HttpLoop ,? HeaderCount ) ?->?
  2. ? ?? case ? gen_tcp : recv ( Socket ,? 0 , ? IDLE_TIMEOUT ) ? of ?
  3. ? ? ? ? { ok ,? http_eoh } ->?
  4. ? ? ? ? ? ?? inet : setopts ( Socket ,? [ { packet ,? raw } ]) ,?
  5. ? ? ? ? ? ?? % 將Socket連接、HTTP請求信息(路徑、請求方法、HTTP版本)以及請求消息頭列表打包成參數化模塊(mochiweb_request)的實例對象?
  6. ? ? ? ? ? ?? Req ?=? mochiweb : new_request ( { Socket ,? Request ,?
  7. ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? lists : reverse ( Headers ) } ) ,? ?
  8. ? ? ? ? ? ?? HttpLoop ( Req ) ,? % 讓用戶編寫的函數處理HTTP請求?
  9. ? ? ? ? ??? case ? Req : should_close () ? of ?
  10. ? ? ? ? ? ? ? ?? true ?->??
  11. ? ? ? ? ? ? ? ? ? ?? gen_tcp : close ( Socket ) ,?
  12. ? ? ? ? ? ? ? ? ? ?? exit ( normal ) ;?
  13. ? ? ? ? ? ? ? ?? false ?->??
  14. ? ? ? ? ? ? ? ? ? ?? Req : cleanup () ,?
  15. ? ? ? ? ? ? ? ? ? ?? mochiweb_http : loop ( Socket ,? HttpLoop ) ?
  16. ? ? ? ? ? ?? end ;?
  17. ? ? ? ? { ok , { http_header ,? _ ,? Name ,? _ ,? Value }} ->?
  18. ? ? ? ? ? ?? headers ( Socket ,? Request ,? [ { margin-
分享到:
評論

Mochiweb的設計分析


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 贡山| 延津县| 辽源市| 宜兰市| 尉氏县| 晋宁县| 广宗县| 佳木斯市| 扎兰屯市| 建平县| 屏东县| 普格县| 珲春市| 石柱| 天门市| 乌苏市| 天全县| 木里| 内黄县| 柳州市| 乌鲁木齐县| 平顺县| 琼结县| 探索| 扬州市| 射阳县| 吉安市| 东兴市| 远安县| 玉林市| 竹山县| 绍兴县| 涡阳县| 松阳县| 灯塔市| 东光县| 鲁甸县| 望江县| 商都县| 洛川县| 邹城市|